Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 7 Mar 2010 23:49:12 +0000 (15:49 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 7 Mar 2010 23:49:12 +0000 (15:49 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6: (368 commits)
  Staging: winbond: fix up wireless api errors
  Staging: dream: camera: sk5k3e2fx: fix code style issues
  Staging: dream: camera: msm_camera: fix code style issues
  Staging: wlan-ng: More checkpatch.pl error cleanups
  Staging: wlan-ng - checkpatch.pl fixups
  Staging: comedi: comedi_fops.c: Checkpatch cleanup
  Staging: comedi: fix suspect code indent in ni_labpc.c
  Staging: comedi: fix yet another brace coding style issue in ni_labpc.c
  Staging: comedi: fix another brace coding style issues in ni_labpc.c
  Staging: comedi: fix brace coding style issue in ni_labpc.c
  Staging: comedi: poc: Adding some KERN_ facility level
  Staging: dream: camera: msm_camera: fix some code style issues
  Staging: wlan-ng: fix most of the style issues in hfa384x.h
  Staging: dream: camera: msm_camera: fix coding style issues
  Staging: comedi: fix bracing coding style and 80 character issues in ni_660x.c
  Staging: comedi: fix bracing coding style issue in ni_65xx.c
  Staging: comedi: pcmad: Checkpatch cleanups
  Staging: comedi: poc: fix coding style issues
  staging: dt3155: revert u_long to u64 usage
  Staging: comedi: drivers.c: Checkpatch cleanup
  ...

977 files changed:
.gitignore
Documentation/ABI/stable/sysfs-devices-node [new file with mode: 0644]
Documentation/cdrom/ide-cd
Documentation/cpu-freq/pcc-cpufreq.txt [new file with mode: 0644]
Documentation/device-mapper/snapshot.txt
Documentation/fault-injection/provoke-crashes.txt [new file with mode: 0644]
Documentation/feature-removal-schedule.txt
Documentation/filesystems/00-INDEX
Documentation/filesystems/Locking
Documentation/filesystems/logfs.txt [new file with mode: 0644]
Documentation/filesystems/nfs/nfs41-server.txt
Documentation/filesystems/proc.txt
Documentation/filesystems/sharedsubtree.txt
Documentation/gpio.txt
Documentation/hwmon/adt7411 [new file with mode: 0644]
Documentation/hwmon/adt7473 [deleted file]
Documentation/hwmon/asc7621 [new file with mode: 0644]
Documentation/hwmon/it87
Documentation/hwmon/lm90
Documentation/init.txt [new file with mode: 0644]
Documentation/kprobes.txt
Documentation/kvm/api.txt
Documentation/power/runtime_pm.txt
Documentation/powerpc/dts-bindings/fsl/dma.txt
Documentation/vm/slub.txt
MAINTAINERS
arch/Kconfig
arch/alpha/kernel/osf_sys.c
arch/arm/include/asm/hardware/iop3xx-adma.h
arch/arm/mach-u300/include/mach/coh901318.h
arch/cris/Kconfig
arch/cris/arch-v32/drivers/cryptocop.c
arch/cris/arch-v32/mach-fs/arbiter.c
arch/cris/kernel/time.c
arch/frv/include/asm/pci.h
arch/ia64/include/asm/elf.h
arch/ia64/kernel/Makefile
arch/ia64/kernel/elfcore.c [new file with mode: 0644]
arch/ia64/kernel/perfmon.c
arch/ia64/kvm/Kconfig
arch/ia64/kvm/kvm-ia64.c
arch/ia64/kvm/kvm_fw.c
arch/ia64/kvm/mmio.c
arch/ia64/kvm/vcpu.c
arch/ia64/mm/init.c
arch/parisc/Kconfig.debug
arch/parisc/include/asm/param.h
arch/parisc/include/asm/system.h
arch/parisc/include/asm/uaccess.h
arch/parisc/include/asm/unistd.h
arch/parisc/kernel/cache.c
arch/parisc/kernel/syscall_table.S
arch/parisc/kernel/time.c
arch/parisc/kernel/unaligned.c
arch/parisc/lib/memcpy.c
arch/powerpc/include/asm/kvm_asm.h
arch/powerpc/include/asm/kvm_book3s.h
arch/powerpc/include/asm/kvm_book3s_64_asm.h
arch/powerpc/include/asm/kvm_e500.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/include/asm/paca.h
arch/powerpc/include/asm/reg.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/ppc_ksyms.c
arch/powerpc/kvm/44x_emulate.c
arch/powerpc/kvm/44x_tlb.c
arch/powerpc/kvm/Kconfig
arch/powerpc/kvm/book3s.c
arch/powerpc/kvm/book3s_64_emulate.c
arch/powerpc/kvm/book3s_64_exports.c
arch/powerpc/kvm/book3s_64_interrupts.S
arch/powerpc/kvm/book3s_64_mmu.c
arch/powerpc/kvm/book3s_64_rmhandlers.S
arch/powerpc/kvm/book3s_64_slb.S
arch/powerpc/kvm/booke.c
arch/powerpc/kvm/booke_emulate.c
arch/powerpc/kvm/e500.c
arch/powerpc/kvm/e500_emulate.c
arch/powerpc/kvm/e500_tlb.c
arch/powerpc/kvm/emulate.c
arch/powerpc/kvm/powerpc.c
arch/powerpc/mm/numa.c
arch/s390/hypfs/inode.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h
arch/sh/boards/mach-migor/setup.c
arch/sh/boot/compressed/cache.c
arch/sh/include/asm/cacheflush.h
arch/sh/include/asm/dma-register.h [new file with mode: 0644]
arch/sh/include/asm/dma-sh.h
arch/sh/include/asm/dmaengine.h [new file with mode: 0644]
arch/sh/include/asm/io.h
arch/sh/include/asm/mmu.h
arch/sh/include/asm/siu.h
arch/sh/include/asm/topology.h
arch/sh/include/cpu-sh3/cpu/dma-register.h [new file with mode: 0644]
arch/sh/include/cpu-sh3/cpu/dma.h
arch/sh/include/cpu-sh4/cpu/dma-register.h [new file with mode: 0644]
arch/sh/include/cpu-sh4/cpu/dma-sh4a.h
arch/sh/include/cpu-sh4/cpu/dma.h
arch/sh/include/mach-migor/mach/migor.h
arch/sh/kernel/cpu/sh4a/setup-sh7722.c
arch/sh/kernel/cpu/sh4a/setup-sh7724.c
arch/sh/kernel/cpu/sh4a/setup-sh7780.c
arch/sh/kernel/cpu/sh4a/setup-sh7785.c
arch/sh/kernel/hw_breakpoint.c
arch/sh/kernel/setup.c
arch/sh/kernel/time.c
arch/sh/lib/libgcc.h
arch/sh/mm/ioremap.c
arch/sh/mm/ioremap_fixed.c
arch/sh/mm/numa.c
arch/sh/mm/pmb.c
arch/sparc/configs/sparc32_defconfig
arch/sparc/configs/sparc64_defconfig
arch/sparc/include/asm/io_32.h
arch/sparc/include/asm/io_64.h
arch/sparc/include/asm/perfctr.h
arch/sparc/include/asm/system_64.h
arch/sparc/include/asm/thread_info_64.h
arch/sparc/kernel/entry.h
arch/sparc/kernel/process_64.c
arch/sparc/kernel/rtrap_64.S
arch/sparc/kernel/sys32.S
arch/sparc/kernel/sys_sparc_64.c
arch/sparc/kernel/syscalls.S
arch/sparc/kernel/systbls.h
arch/sparc/kernel/systbls_64.S
arch/sparc/kernel/traps_64.c
arch/sparc/prom/p1275.c
arch/um/.gitignore [new file with mode: 0644]
arch/um/drivers/line.c
arch/um/drivers/mconsole_kern.c
arch/um/sys-i386/Makefile
arch/um/sys-i386/asm/elf.h
arch/um/sys-i386/elfcore.c [new file with mode: 0644]
arch/x86/Kconfig
arch/x86/include/asm/Kbuild
arch/x86/include/asm/alternative.h
arch/x86/include/asm/hyperv.h [new file with mode: 0644]
arch/x86/include/asm/kprobes.h
arch/x86/include/asm/kvm_emulate.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/kvm_para.h
arch/x86/include/asm/svm.h
arch/x86/include/asm/vmx.h
arch/x86/kernel/alternative.c
arch/x86/kernel/cpu/cpufreq/Kconfig
arch/x86/kernel/cpu/cpufreq/Makefile
arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c [new file with mode: 0644]
arch/x86/kernel/cpu/mtrr/main.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/kprobes.c
arch/x86/kernel/vsyscall_64.c
arch/x86/kvm/Kconfig
arch/x86/kvm/emulate.c
arch/x86/kvm/i8254.c
arch/x86/kvm/i8254.h
arch/x86/kvm/i8259.c
arch/x86/kvm/irq.h
arch/x86/kvm/kvm_cache_regs.h
arch/x86/kvm/lapic.c
arch/x86/kvm/lapic.h
arch/x86/kvm/mmu.c
arch/x86/kvm/mmu.h
arch/x86/kvm/paging_tmpl.h
arch/x86/kvm/svm.c
arch/x86/kvm/trace.h
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h
crypto/ahash.c
crypto/authenc.c
crypto/md5.c
drivers/Kconfig
drivers/acpi/processor_core.c
drivers/base/power/Makefile
drivers/base/power/generic_ops.c [new file with mode: 0644]
drivers/char/agp/intel-agp.c
drivers/clocksource/Kconfig [deleted file]
drivers/cpuidle/governors/menu.c
drivers/dma/Kconfig
drivers/dma/Makefile
drivers/dma/coh901318.c
drivers/dma/coh901318_lli.c
drivers/dma/dmatest.c
drivers/dma/fsldma.c
drivers/dma/fsldma.h
drivers/dma/ioat/dma.c
drivers/dma/ioat/dma.h
drivers/dma/ioat/dma_v2.c
drivers/dma/ioat/dma_v2.h
drivers/dma/ioat/dma_v3.c
drivers/dma/ioat/registers.h
drivers/dma/ipu/ipu_idmac.c
drivers/dma/mpc512x_dma.c [new file with mode: 0644]
drivers/dma/ppc4xx/adma.c
drivers/dma/shdma.c
drivers/dma/shdma.h
drivers/eisa/eisa-bus.c
drivers/firmware/memmap.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/cs5535-gpio.c
drivers/gpio/gpiolib.c
drivers/gpio/it8761e_gpio.c [new file with mode: 0644]
drivers/gpio/max7300.c [new file with mode: 0644]
drivers/gpio/max7301.c
drivers/gpio/max730x.c [new file with mode: 0644]
drivers/gpio/pca953x.c
drivers/gpio/pl061.c
drivers/gpio/timbgpio.c
drivers/gpu/drm/Makefile
drivers/gpu/drm/drm_buffer.c [new file with mode: 0644]
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_bios.c
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_overlay.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/nouveau_acpi.c
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.h
drivers/gpu/drm/nouveau/nouveau_calc.c
drivers/gpu/drm/nouveau/nouveau_channel.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.h
drivers/gpu/drm/nouveau/nouveau_debugfs.c
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nouveau_dma.c
drivers/gpu/drm/nouveau/nouveau_dma.h
drivers/gpu/drm/nouveau/nouveau_drv.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/nouveau/nouveau_gem.c
drivers/gpu/drm/nouveau/nouveau_hw.c
drivers/gpu/drm/nouveau/nouveau_i2c.c
drivers/gpu/drm/nouveau/nouveau_irq.c
drivers/gpu/drm/nouveau/nouveau_notifier.c
drivers/gpu/drm/nouveau/nouveau_state.c
drivers/gpu/drm/nouveau/nv04_crtc.c
drivers/gpu/drm/nouveau/nv04_dac.c
drivers/gpu/drm/nouveau/nv04_dfp.c
drivers/gpu/drm/nouveau/nv04_display.c
drivers/gpu/drm/nouveau/nv04_fbcon.c
drivers/gpu/drm/nouveau/nv04_fifo.c
drivers/gpu/drm/nouveau/nv04_tv.c
drivers/gpu/drm/nouveau/nv17_tv.c
drivers/gpu/drm/nouveau/nv40_fifo.c
drivers/gpu/drm/nouveau/nv50_crtc.c
drivers/gpu/drm/nouveau/nv50_dac.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_fbcon.c
drivers/gpu/drm/nouveau/nv50_fifo.c
drivers/gpu/drm/nouveau/nv50_graph.c
drivers/gpu/drm/nouveau/nv50_grctx.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nv50_instmem.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atom.c
drivers/gpu/drm/radeon/atombios.h
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/avivod.h
drivers/gpu/drm/radeon/evergreen.c [new file with mode: 0644]
drivers/gpu/drm/radeon/evergreen_reg.h [new file with mode: 0644]
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r200.c
drivers/gpu/drm/radeon/r300.c
drivers/gpu/drm/radeon/r300_cmdbuf.c
drivers/gpu/drm/radeon/r300_reg.h
drivers/gpu/drm/radeon/r420.c
drivers/gpu/drm/radeon/r500_reg.h
drivers/gpu/drm/radeon/r520.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_audio.c
drivers/gpu/drm/radeon/r600_blit.c
drivers/gpu/drm/radeon/r600_blit_kms.c
drivers/gpu/drm/radeon/r600_blit_shaders.c
drivers/gpu/drm/radeon/r600_cp.c
drivers/gpu/drm/radeon/r600_cs.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_agp.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_atombios.c
drivers/gpu/drm/radeon/radeon_atpx_handler.c [new file with mode: 0644]
drivers/gpu/drm/radeon/radeon_bios.c
drivers/gpu/drm/radeon/radeon_clocks.c
drivers/gpu/drm/radeon/radeon_combios.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cp.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_cursor.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_drv.h
drivers/gpu/drm/radeon/radeon_encoders.c
drivers/gpu/drm/radeon/radeon_family.h
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_gart.c
drivers/gpu/drm/radeon/radeon_gem.c
drivers/gpu/drm/radeon/radeon_i2c.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_legacy_crtc.c
drivers/gpu/drm/radeon/radeon_legacy_encoders.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/radeon_object.c
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_reg.h
drivers/gpu/drm/radeon/radeon_ring.c
drivers/gpu/drm/radeon/radeon_state.c
drivers/gpu/drm/radeon/radeon_test.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/radeon/reg_srcs/r600 [new file with mode: 0644]
drivers/gpu/drm/radeon/rs400.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rs690.c
drivers/gpu/drm/radeon/rv515.c
drivers/gpu/drm/radeon/rv770.c
drivers/gpu/drm/radeon/rv770d.h
drivers/gpu/drm/ttm/ttm_tt.c
drivers/gpu/vga/Kconfig
drivers/gpu/vga/Makefile
drivers/gpu/vga/vga_switcheroo.c [new file with mode: 0644]
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/adcxx.c
drivers/hwmon/adt7411.c [new file with mode: 0644]
drivers/hwmon/adt7473.c [deleted file]
drivers/hwmon/asc7621.c [new file with mode: 0644]
drivers/hwmon/fschmd.c
drivers/hwmon/g760a.c
drivers/hwmon/it87.c
drivers/hwmon/lm90.c
drivers/hwmon/tmp401.c
drivers/hwmon/tmp421.c
drivers/hwmon/vt8231.c
drivers/hwmon/w83793.c
drivers/i2c/busses/i2c-designware.c
drivers/ide/aec62xx.c
drivers/ide/ali14xx.c
drivers/ide/alim15x3.c
drivers/ide/amd74xx.c
drivers/ide/at91_ide.c
drivers/ide/atiixp.c
drivers/ide/au1xxx-ide.c
drivers/ide/cmd640.c
drivers/ide/cmd64x.c
drivers/ide/cs5520.c
drivers/ide/cs5530.c
drivers/ide/cs5535.c
drivers/ide/cs5536.c
drivers/ide/cy82c693.c
drivers/ide/dtc2278.c
drivers/ide/hpt366.c
drivers/ide/ht6560b.c
drivers/ide/icside.c
drivers/ide/ide-cs.c
drivers/ide/ide-devsets.c
drivers/ide/ide-iops.c
drivers/ide/ide-probe.c
drivers/ide/ide-tape.c
drivers/ide/ide-timings.c
drivers/ide/ide-xfer-mode.c
drivers/ide/it8172.c
drivers/ide/it8213.c
drivers/ide/it821x.c
drivers/ide/jmicron.c
drivers/ide/opti621.c
drivers/ide/palm_bk3710.c
drivers/ide/pdc202xx_new.c
drivers/ide/pdc202xx_old.c
drivers/ide/piix.c
drivers/ide/pmac.c
drivers/ide/qd65xx.c
drivers/ide/sc1200.c
drivers/ide/scc_pata.c
drivers/ide/serverworks.c
drivers/ide/sgiioc4.c
drivers/ide/siimage.c
drivers/ide/sis5513.c
drivers/ide/sl82c105.c
drivers/ide/slc90e66.c
drivers/ide/tc86c001.c
drivers/ide/triflex.c
drivers/ide/tx4938ide.c
drivers/ide/tx4939ide.c
drivers/ide/umc8672.c
drivers/ide/via82cxxx.c
drivers/infiniband/core/mad.c
drivers/infiniband/core/uverbs.h
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/input/touchscreen/mc13783_ts.c
drivers/md/dm-crypt.c
drivers/md/dm-delay.c
drivers/md/dm-ioctl.c
drivers/md/dm-linear.c
drivers/md/dm-log.c
drivers/md/dm-mpath.c
drivers/md/dm-raid1.c
drivers/md/dm-snap.c
drivers/md/dm-stripe.c
drivers/md/dm-table.c
drivers/md/dm-uevent.c
drivers/md/dm.c
drivers/md/dm.h
drivers/mfd/htc-egpio.c
drivers/mfd/mc13783-core.c
drivers/misc/Kconfig
drivers/misc/iwmc3200top/main.c
drivers/misc/lkdtm.c
drivers/misc/sgi-xp/xpnet.c
drivers/mmc/core/core.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_io.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/at91_mci.c
drivers/mmc/host/bfin_sdh.c
drivers/mmc/host/davinci_mmc.c
drivers/mmc/host/ricoh_mmc.c [deleted file]
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci.c
drivers/mtd/ubi/build.c
drivers/mtd/ubi/debug.h
drivers/mtd/ubi/io.c
drivers/mtd/ubi/scan.c
drivers/mtd/ubi/wl.c
drivers/net/gianfar.c
drivers/net/ixgbe/ixgbe_main.c
drivers/net/ixgbevf/ixgbevf_main.c
drivers/net/wireless/ath/ar9170/main.c
drivers/net/wireless/iwmc3200wifi/debugfs.c
drivers/net/wireless/iwmc3200wifi/rx.c
drivers/parisc/eisa_enumerator.c
drivers/parisc/superio.c
drivers/pci/quirks.c
drivers/pcmcia/Kconfig
drivers/pcmcia/cardbus.c
drivers/pcmcia/cistpl.c
drivers/pcmcia/db1xxx_ss.c
drivers/pcmcia/pd6729.c
drivers/pcmcia/rsrc_mgr.c
drivers/pcmcia/xxs1500_ss.c
drivers/pcmcia/yenta_socket.c
drivers/power/Kconfig
drivers/power/bq27x00_battery.c
drivers/power/da9030_battery.c
drivers/power/wm97xx_battery.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/ab3100.c
drivers/regulator/core.c
drivers/regulator/dummy.c [new file with mode: 0644]
drivers/regulator/dummy.h [new file with mode: 0644]
drivers/regulator/fixed.c
drivers/regulator/lp3971.c
drivers/regulator/max1586.c
drivers/regulator/max8649.c [new file with mode: 0644]
drivers/regulator/max8660.c
drivers/regulator/mc13783-regulator.c
drivers/regulator/pcap-regulator.c
drivers/regulator/tps65023-regulator.c
drivers/regulator/tps6507x-regulator.c
drivers/regulator/twl-regulator.c
drivers/regulator/virtual.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-isink.c
drivers/regulator/wm831x-ldo.c
drivers/regulator/wm8350-regulator.c
drivers/regulator/wm8400-regulator.c
drivers/regulator/wm8994-regulator.c [new file with mode: 0644]
drivers/rtc/class.c
drivers/rtc/rtc-at91sam9.c
drivers/rtc/rtc-coh901331.c
drivers/rtc/rtc-ep93xx.c
drivers/rtc/rtc-mc13783.c
drivers/rtc/rtc-mxc.c
drivers/rtc/rtc-pcf2123.c
drivers/rtc/rtc-twl.c
drivers/serial/Kconfig
drivers/serial/sh-sci.c
drivers/staging/pohmelfs/inode.c
drivers/usb/core/devices.c
drivers/usb/gadget/f_mass_storage.c
drivers/usb/gadget/file_storage.c
drivers/video/console/Kconfig
drivers/video/console/fbcon.c
drivers/video/console/vgacon.c
drivers/video/fbmem.c
drivers/virtio/virtio_pci.c
drivers/xen/Kconfig
fs/9p/fid.c
fs/9p/v9fs.c
fs/9p/v9fs.h
fs/9p/vfs_dir.c
fs/9p/vfs_file.c
fs/9p/vfs_inode.c
fs/Kconfig
fs/Makefile
fs/adfs/adfs.h
fs/adfs/inode.c
fs/affs/affs.h
fs/affs/inode.c
fs/afs/internal.h
fs/afs/super.c
fs/afs/write.c
fs/attr.c
fs/autofs4/autofs_i.h
fs/autofs4/dev-ioctl.c
fs/autofs4/expire.c
fs/autofs4/inode.c
fs/autofs4/root.c
fs/bfs/inode.c
fs/binfmt_aout.c
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c
fs/binfmt_flat.c
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/cifs/file.c
fs/compat_binfmt_elf.c
fs/compat_ioctl.c
fs/dcache.c
fs/debugfs/inode.c
fs/exec.c
fs/exofs/common.h
fs/exofs/exofs.h
fs/exofs/inode.c
fs/exofs/ios.c
fs/exofs/super.c
fs/ext2/balloc.c
fs/ext2/ext2.h
fs/ext2/file.c
fs/ext2/ialloc.c
fs/ext2/inode.c
fs/ext2/namei.c
fs/ext2/super.c
fs/ext2/xattr.c
fs/ext3/balloc.c
fs/ext3/file.c
fs/ext3/ialloc.c
fs/ext3/inode.c
fs/ext3/namei.c
fs/ext3/super.c
fs/ext3/xattr.c
fs/ext4/balloc.c
fs/ext4/block_validity.c
fs/ext4/dir.c
fs/ext4/ext4.h
fs/ext4/ext4_jbd2.c
fs/ext4/ext4_jbd2.h
fs/ext4/extents.c
fs/ext4/file.c
fs/ext4/fsync.c
fs/ext4/ialloc.c
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/mballoc.c
fs/ext4/mballoc.h
fs/ext4/migrate.c
fs/ext4/move_extent.c
fs/ext4/namei.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/xattr.c
fs/fat/inode.c
fs/fcntl.c
fs/file.c
fs/file_table.c
fs/fs-writeback.c
fs/gfs2/ops_inode.c
fs/gfs2/quota.c
fs/gfs2/quota.h
fs/gfs2/super.c
fs/gfs2/sys.c
fs/hfs/hfs_fs.h
fs/hfs/inode.c
fs/hfsplus/super.c
fs/hpfs/anode.c
fs/hpfs/dentry.c
fs/hpfs/dir.c
fs/hpfs/dnode.c
fs/hpfs/ea.c
fs/hpfs/hpfs_fn.h
fs/hpfs/inode.c
fs/hpfs/map.c
fs/hpfs/name.c
fs/hpfs/namei.c
fs/hppfs/hppfs.c
fs/inode.c
fs/internal.h
fs/jbd/commit.c
fs/jbd/transaction.c
fs/jbd2/checkpoint.c
fs/jbd2/commit.c
fs/jbd2/journal.c
fs/jbd2/transaction.c
fs/jfs/acl.c
fs/jfs/file.c
fs/jfs/inode.c
fs/jfs/jfs_acl.h
fs/jfs/jfs_dtree.c
fs/jfs/jfs_extent.c
fs/jfs/jfs_inode.c
fs/jfs/jfs_inode.h
fs/jfs/jfs_xtree.c
fs/jfs/namei.c
fs/jfs/super.c
fs/jfs/xattr.c
fs/libfs.c
fs/lockd/host.c
fs/lockd/mon.c
fs/lockd/svc.c
fs/locks.c
fs/logfs/Kconfig [new file with mode: 0644]
fs/logfs/Makefile [new file with mode: 0644]
fs/logfs/compr.c [new file with mode: 0644]
fs/logfs/dev_bdev.c [new file with mode: 0644]
fs/logfs/dev_mtd.c [new file with mode: 0644]
fs/logfs/dir.c [new file with mode: 0644]
fs/logfs/file.c [new file with mode: 0644]
fs/logfs/gc.c [new file with mode: 0644]
fs/logfs/inode.c [new file with mode: 0644]
fs/logfs/journal.c [new file with mode: 0644]
fs/logfs/logfs.h [new file with mode: 0644]
fs/logfs/logfs_abi.h [new file with mode: 0644]
fs/logfs/readwrite.c [new file with mode: 0644]
fs/logfs/segment.c [new file with mode: 0644]
fs/logfs/super.c [new file with mode: 0644]
fs/minix/inode.c
fs/namei.c
fs/namespace.c
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/dir.c
fs/nfs/dns_resolve.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/nfs3proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4renewd.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/proc.c
fs/nfs/symlink.c
fs/nfs/write.c
fs/nfsctl.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4recover.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/vfs.c
fs/nilfs2/dir.c
fs/nilfs2/namei.c
fs/nilfs2/nilfs.h
fs/notify/inotify/inotify_user.c
fs/ntfs/dir.c
fs/ntfs/file.c
fs/ntfs/inode.c
fs/ntfs/inode.h
fs/ntfs/super.c
fs/ocfs2/alloc.c
fs/ocfs2/aops.c
fs/ocfs2/dir.c
fs/ocfs2/file.c
fs/ocfs2/inode.c
fs/ocfs2/namei.c
fs/ocfs2/quota_global.c
fs/ocfs2/quota_local.c
fs/ocfs2/refcounttree.c
fs/omfs/inode.c
fs/open.c
fs/pnode.c
fs/pnode.h
fs/proc/array.c
fs/proc/base.c
fs/proc/generic.c
fs/proc/root.c
fs/proc/task_mmu.c
fs/quota/Kconfig
fs/quota/Makefile
fs/quota/compat.c [new file with mode: 0644]
fs/quota/dquot.c
fs/quota/netlink.c [new file with mode: 0644]
fs/quota/quota.c
fs/reiserfs/bitmap.c
fs/reiserfs/file.c
fs/reiserfs/inode.c
fs/reiserfs/namei.c
fs/reiserfs/stree.c
fs/reiserfs/super.c
fs/reiserfs/xattr.c
fs/select.c
fs/seq_file.c
fs/squashfs/Makefile
fs/squashfs/block.c
fs/squashfs/cache.c
fs/squashfs/decompressor.c [new file with mode: 0644]
fs/squashfs/decompressor.h [new file with mode: 0644]
fs/squashfs/dir.c
fs/squashfs/export.c
fs/squashfs/file.c
fs/squashfs/fragment.c
fs/squashfs/id.c
fs/squashfs/inode.c
fs/squashfs/namei.c
fs/squashfs/squashfs.h
fs/squashfs/squashfs_fs.h
fs/squashfs/squashfs_fs_sb.h
fs/squashfs/super.c
fs/squashfs/symlink.c
fs/squashfs/zlib_wrapper.c [new file with mode: 0644]
fs/super.c
fs/sync.c
fs/sysv/inode.c
fs/sysv/sysv.h
fs/ubifs/dir.c
fs/ubifs/file.c
fs/ubifs/super.c
fs/udf/balloc.c
fs/udf/dir.c
fs/udf/file.c
fs/udf/ialloc.c
fs/udf/inode.c
fs/udf/namei.c
fs/udf/symlink.c
fs/udf/udfdecl.h
fs/ufs/balloc.c
fs/ufs/dir.c
fs/ufs/file.c
fs/ufs/ialloc.c
fs/ufs/inode.c
fs/ufs/namei.c
fs/ufs/super.c
fs/ufs/truncate.c
fs/ufs/ufs.h
fs/xfs/Makefile
fs/xfs/linux-2.6/xfs_aops.c
fs/xfs/linux-2.6/xfs_export.c
fs/xfs/linux-2.6/xfs_file.c
fs/xfs/linux-2.6/xfs_iops.c
fs/xfs/linux-2.6/xfs_linux.h
fs/xfs/linux-2.6/xfs_lrw.c [deleted file]
fs/xfs/linux-2.6/xfs_lrw.h [deleted file]
fs/xfs/linux-2.6/xfs_quotaops.c
fs/xfs/linux-2.6/xfs_super.c
fs/xfs/linux-2.6/xfs_sync.c
fs/xfs/linux-2.6/xfs_trace.c
fs/xfs/linux-2.6/xfs_trace.h
fs/xfs/xfs_bmap.c
fs/xfs/xfs_fs.h
fs/xfs/xfs_iget.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_log.c
fs/xfs/xfs_log.h
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_trans.c
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_buf.c
fs/xfs/xfs_vnodeops.c
fs/xfs/xfs_vnodeops.h
include/asm-generic/gpio.h
include/drm/drmP.h
include/drm/drm_buffer.h [new file with mode: 0644]
include/drm/drm_crtc.h
include/drm/drm_edid.h
include/drm/drm_pciids.h
include/drm/nouveau_drm.h
include/drm/radeon_drm.h
include/drm/ttm/ttm_bo_driver.h
include/linux/audit.h
include/linux/binfmts.h
include/linux/bitops.h
include/linux/btree-128.h [new file with mode: 0644]
include/linux/btree-type.h [new file with mode: 0644]
include/linux/btree.h [new file with mode: 0644]
include/linux/coredump.h [new file with mode: 0644]
include/linux/cpumask.h
include/linux/device-mapper.h
include/linux/dm-io.h
include/linux/dm-ioctl.h
include/linux/dmaengine.h
include/linux/elf.h
include/linux/elfcore.h
include/linux/exportfs.h
include/linux/ext3_fs.h
include/linux/ext3_fs_i.h
include/linux/fault-inject.h
include/linux/fb.h
include/linux/firmware-map.h
include/linux/fs.h
include/linux/fsnotify.h
include/linux/gfp.h
include/linux/i2c/pca953x.h
include/linux/ide.h
include/linux/jbd.h
include/linux/jbd2.h
include/linux/kprobes.h
include/linux/kvm.h
include/linux/kvm_host.h
include/linux/list.h
include/linux/magic.h
include/linux/mfd/mc13783.h
include/linux/mm.h
include/linux/mm_types.h
include/linux/mmc/card.h
include/linux/mmc/host.h
include/linux/mmc/pm.h [new file with mode: 0644]
include/linux/mmc/sdio.h
include/linux/mmc/sdio_func.h
include/linux/mmzone.h
include/linux/mnt_namespace.h
include/linux/mount.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nodemask.h
include/linux/pm.h
include/linux/pm_runtime.h
include/linux/quota.h
include/linux/quotaops.h
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/linux/regulator/fixed.h
include/linux/regulator/max8649.h [new file with mode: 0644]
include/linux/reiserfs_fs.h
include/linux/rmap.h
include/linux/sched.h
include/linux/serial_sci.h
include/linux/slab.h
include/linux/slub_def.h
include/linux/smp.h
include/linux/spi/max7301.h
include/linux/sunrpc/bc_xprt.h
include/linux/vga_switcheroo.h [new file with mode: 0644]
include/linux/virtio_9p.h
include/net/9p/client.h
include/trace/events/ext4.h
include/trace/events/jbd2.h
include/trace/events/kvm.h
init/do_mounts_initrd.c
init/initramfs.c
init/main.c
ipc/mqueue.c
kernel/Makefile
kernel/audit_tree.c
kernel/auditsc.c
kernel/cpu.c
kernel/elfcore.c [new file with mode: 0644]
kernel/exit.c
kernel/fork.c
kernel/kprobes.c
kernel/padata.c
kernel/panic.c
kernel/params.c
kernel/perf_event.c
kernel/pid.c
kernel/posix-cpu-timers.c
kernel/power/hibernate.c
kernel/power/suspend.c
kernel/printk.c
kernel/relay.c
kernel/sched.c
kernel/sched_cpupri.c
kernel/sched_rt.c
kernel/signal.c
kernel/sys.c
kernel/sysctl.c
kernel/sysctl_binary.c
kernel/tsacct.c
lib/Kconfig
lib/Kconfig.debug
lib/Makefile
lib/bitmap.c
lib/btree.c [new file with mode: 0644]
lib/crc32.c
lib/list_sort.c
lib/show_mem.c
lib/string.c
lib/vsprintf.c
mm/fadvise.c
mm/failslab.c
mm/filemap.c
mm/filemap_xip.c
mm/fremap.c
mm/ksm.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/migrate.c
mm/mlock.c
mm/mmap.c
mm/mremap.c
mm/nommu.c
mm/oom_kill.c
mm/page_alloc.c
mm/readahead.c
mm/rmap.c
mm/slab.c
mm/slub.c
mm/swap.c
mm/swapfile.c
mm/vmscan.c
mm/vmstat.c
net/9p/client.c
net/9p/protocol.c
net/9p/protocol.h
net/9p/trans_virtio.c
net/sunrpc/addr.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/rpc_pipe.c
net/sunrpc/svc.c
net/sunrpc/svc_xprt.c
net/sunrpc/svcauth_unix.c
net/sunrpc/svcsock.c
net/sunrpc/xprtsock.c
scripts/checkpatch.pl
scripts/get_maintainer.pl
security/smack/smack_lsm.c
security/tomoyo/realpath.c
sound/soc/codecs/uda1380.c
sound/soc/sh/siu.h
sound/soc/sh/siu_pcm.c
tools/perf/Documentation/perf-probe.txt
tools/perf/Makefile
tools/perf/builtin-probe.c
tools/perf/util/probe-event.c
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h
tools/perf/util/string.c
tools/perf/util/string.h
virt/kvm/Kconfig
virt/kvm/assigned-dev.c
virt/kvm/coalesced_mmio.c
virt/kvm/coalesced_mmio.h
virt/kvm/eventfd.c
virt/kvm/ioapic.c
virt/kvm/ioapic.h
virt/kvm/iommu.c
virt/kvm/kvm_main.c

index de6344e15706280de410cd83372f997720d0947a..efab0ebec85993afd1ee2a3a507722b7d0e223a2 100644 (file)
@@ -36,6 +36,7 @@ modules.builtin
 #
 tags
 TAGS
+linux
 vmlinux
 vmlinuz
 System.map
diff --git a/Documentation/ABI/stable/sysfs-devices-node b/Documentation/ABI/stable/sysfs-devices-node
new file mode 100644 (file)
index 0000000..49b82ca
--- /dev/null
@@ -0,0 +1,7 @@
+What:          /sys/devices/system/node/nodeX
+Date:          October 2002
+Contact:       Linux Memory Management list <linux-mm@kvack.org>
+Description:
+               When CONFIG_NUMA is enabled, this is a directory containing
+               information on node X such as what CPUs are local to the
+               node.
index 2c558cd6c1ef605f11dfe2a1dff1be292946d57e..f4dc9de2694e90019ea8377537dfe40aeef1df8e 100644 (file)
@@ -159,42 +159,7 @@ two arguments:  the CDROM device, and the slot number to which you wish
 to change.  If the slot number is -1, the drive is unloaded.
 
 
-4. Compilation options
-----------------------
-
-There are a few additional options which can be set when compiling the
-driver.  Most people should not need to mess with any of these; they
-are listed here simply for completeness.  A compilation option can be
-enabled by adding a line of the form `#define <option> 1' to the top
-of ide-cd.c.  All these options are disabled by default.
-
-VERBOSE_IDE_CD_ERRORS
-  If this is set, ATAPI error codes will be translated into textual
-  descriptions.  In addition, a dump is made of the command which
-  provoked the error.  This is off by default to save the memory used
-  by the (somewhat long) table of error descriptions.  
-
-STANDARD_ATAPI
-  If this is set, the code needed to deal with certain drives which do
-  not properly implement the ATAPI spec will be disabled.  If you know
-  your drive implements ATAPI properly, you can turn this on to get a
-  slightly smaller kernel.
-
-NO_DOOR_LOCKING
-  If this is set, the driver will never attempt to lock the door of
-  the drive.
-
-CDROM_NBLOCKS_BUFFER
-  This sets the size of the buffer to be used for a CDROMREADAUDIO
-  ioctl.  The default is 8.
-
-TEST
-  This currently enables an additional ioctl which enables a user-mode
-  program to execute an arbitrary packet command.  See the source for
-  details.  This should be left off unless you know what you're doing.
-
-
-5. Common problems
+4. Common problems
 ------------------
 
 This section discusses some common problems encountered when trying to
@@ -371,7 +336,7 @@ f. Data corruption.
     expense of low system performance.
 
 
-6. cdchange.c
+5. cdchange.c
 -------------
 
 /*
diff --git a/Documentation/cpu-freq/pcc-cpufreq.txt b/Documentation/cpu-freq/pcc-cpufreq.txt
new file mode 100644 (file)
index 0000000..9e3c3b3
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *  pcc-cpufreq.txt - PCC interface documentation
+ *
+ *  Copyright (C) 2009 Red Hat, Matthew Garrett <mjg@redhat.com>
+ *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *      Nagananda Chumbalkar <nagananda.chumbalkar@hp.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; 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, GOOD TITLE or NON
+ *  INFRINGEMENT. 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+
+                       Processor Clocking Control Driver
+                       ---------------------------------
+
+Contents:
+---------
+1.     Introduction
+1.1    PCC interface
+1.1.1   Get Average Frequency
+1.1.2  Set Desired Frequency
+1.2    Platforms affected
+2.     Driver and /sys details
+2.1    scaling_available_frequencies
+2.2    cpuinfo_transition_latency
+2.3    cpuinfo_cur_freq
+2.4    related_cpus
+3.     Caveats
+
+1. Introduction:
+----------------
+Processor Clocking Control (PCC) is an interface between the platform
+firmware and OSPM. It is a mechanism for coordinating processor
+performance (ie: frequency) between the platform firmware and the OS.
+
+The PCC driver (pcc-cpufreq) allows OSPM to take advantage of the PCC
+interface.
+
+OS utilizes the PCC interface to inform platform firmware what frequency the
+OS wants for a logical processor. The platform firmware attempts to achieve
+the requested frequency. If the request for the target frequency could not be
+satisfied by platform firmware, then it usually means that power budget
+conditions are in place, and "power capping" is taking place.
+
+1.1 PCC interface:
+------------------
+The complete PCC specification is available here:
+http://www.acpica.org/download/Processor-Clocking-Control-v1p0.pdf
+
+PCC relies on a shared memory region that provides a channel for communication
+between the OS and platform firmware. PCC also implements a "doorbell" that
+is used by the OS to inform the platform firmware that a command has been
+sent.
+
+The ACPI PCCH() method is used to discover the location of the PCC shared
+memory region. The shared memory region header contains the "command" and
+"status" interface. PCCH() also contains details on how to access the platform
+doorbell.
+
+The following commands are supported by the PCC interface:
+* Get Average Frequency
+* Set Desired Frequency
+
+The ACPI PCCP() method is implemented for each logical processor and is
+used to discover the offsets for the input and output buffers in the shared
+memory region.
+
+When PCC mode is enabled, the platform will not expose processor performance
+or throttle states (_PSS, _TSS and related ACPI objects) to OSPM. Therefore,
+the native P-state driver (such as acpi-cpufreq for Intel, powernow-k8 for
+AMD) will not load.
+
+However, OSPM remains in control of policy. The governor (eg: "ondemand")
+computes the required performance for each processor based on server workload.
+The PCC driver fills in the command interface, and the input buffer and
+communicates the request to the platform firmware. The platform firmware is
+responsible for delivering the requested performance.
+
+Each PCC command is "global" in scope and can affect all the logical CPUs in
+the system. Therefore, PCC is capable of performing "group" updates. With PCC
+the OS is capable of getting/setting the frequency of all the logical CPUs in
+the system with a single call to the BIOS.
+
+1.1.1 Get Average Frequency:
+----------------------------
+This command is used by the OSPM to query the running frequency of the
+processor since the last time this command was completed. The output buffer
+indicates the average unhalted frequency of the logical processor expressed as
+a percentage of the nominal (ie: maximum) CPU frequency. The output buffer
+also signifies if the CPU frequency is limited by a power budget condition.
+
+1.1.2 Set Desired Frequency:
+----------------------------
+This command is used by the OSPM to communicate to the platform firmware the
+desired frequency for a logical processor. The output buffer is currently
+ignored by OSPM. The next invocation of "Get Average Frequency" will inform
+OSPM if the desired frequency was achieved or not.
+
+1.2 Platforms affected:
+-----------------------
+The PCC driver will load on any system where the platform firmware:
+* supports the PCC interface, and the associated PCCH() and PCCP() methods
+* assumes responsibility for managing the hardware clocking controls in order
+to deliver the requested processor performance
+
+Currently, certain HP ProLiant platforms implement the PCC interface. On those
+platforms PCC is the "default" choice.
+
+However, it is possible to disable this interface via a BIOS setting. In
+such an instance, as is also the case on platforms where the PCC interface
+is not implemented, the PCC driver will fail to load silently.
+
+2. Driver and /sys details:
+---------------------------
+When the driver loads, it merely prints the lowest and the highest CPU
+frequencies supported by the platform firmware.
+
+The PCC driver loads with a message such as:
+pcc-cpufreq: (v1.00.00) driver loaded with frequency limits: 1600 MHz, 2933
+MHz
+
+This means that the OPSM can request the CPU to run at any frequency in
+between the limits (1600 MHz, and 2933 MHz) specified in the message.
+
+Internally, there is no need for the driver to convert the "target" frequency
+to a corresponding P-state.
+
+The VERSION number for the driver will be of the format v.xy.ab.
+eg: 1.00.02
+   ----- --
+    |    |
+    |    -- this will increase with bug fixes/enhancements to the driver
+    |-- this is the version of the PCC specification the driver adheres to
+
+
+The following is a brief discussion on some of the fields exported via the
+/sys filesystem and how their values are affected by the PCC driver:
+
+2.1 scaling_available_frequencies:
+----------------------------------
+scaling_available_frequencies is not created in /sys. No intermediate
+frequencies need to be listed because the BIOS will try to achieve any
+frequency, within limits, requested by the governor. A frequency does not have
+to be strictly associated with a P-state.
+
+2.2 cpuinfo_transition_latency:
+-------------------------------
+The cpuinfo_transition_latency field is 0. The PCC specification does
+not include a field to expose this value currently.
+
+2.3 cpuinfo_cur_freq:
+---------------------
+A) Often cpuinfo_cur_freq will show a value different than what is declared
+in the scaling_available_frequencies or scaling_cur_freq, or scaling_max_freq.
+This is due to "turbo boost" available on recent Intel processors. If certain
+conditions are met the BIOS can achieve a slightly higher speed than requested
+by OSPM. An example:
+
+scaling_cur_freq       : 2933000
+cpuinfo_cur_freq       : 3196000
+
+B) There is a round-off error associated with the cpuinfo_cur_freq value.
+Since the driver obtains the current frequency as a "percentage" (%) of the
+nominal frequency from the BIOS, sometimes, the values displayed by
+scaling_cur_freq and cpuinfo_cur_freq may not match. An example:
+
+scaling_cur_freq       : 1600000
+cpuinfo_cur_freq       : 1583000
+
+In this example, the nominal frequency is 2933 MHz. The driver obtains the
+current frequency, cpuinfo_cur_freq, as 54% of the nominal frequency:
+
+       54% of 2933 MHz = 1583 MHz
+
+Nominal frequency is the maximum frequency of the processor, and it usually
+corresponds to the frequency of the P0 P-state.
+
+2.4 related_cpus:
+-----------------
+The related_cpus field is identical to affected_cpus.
+
+affected_cpus  : 4
+related_cpus   : 4
+
+Currently, the PCC driver does not evaluate _PSD. The platforms that support
+PCC do not implement SW_ALL. So OSPM doesn't need to perform any coordination
+to ensure that the same frequency is requested of all dependent CPUs.
+
+3. Caveats:
+-----------
+The "cpufreq_stats" module in its present form cannot be loaded and
+expected to work with the PCC driver. Since the "cpufreq_stats" module
+provides information wrt each P-state, it is not applicable to the PCC driver.
index e3a77b215135eeef2350fc0471dfe8633184384e..0d5bc46dc1676869358cd6f36975905031f17f9e 100644 (file)
@@ -122,3 +122,47 @@ volumeGroup-base: 0 2097152 snapshot-merge 254:11 254:12 P 16
 brw-------  1 root root 254, 11 29 ago 18:15 /dev/mapper/volumeGroup-base-real
 brw-------  1 root root 254, 12 29 ago 18:16 /dev/mapper/volumeGroup-base-cow
 brw-------  1 root root 254, 10 29 ago 18:16 /dev/mapper/volumeGroup-base
+
+
+How to determine when a merging is complete
+===========================================
+The snapshot-merge and snapshot status lines end with:
+  <sectors_allocated>/<total_sectors> <metadata_sectors>
+
+Both <sectors_allocated> and <total_sectors> include both data and metadata.
+During merging, the number of sectors allocated gets smaller and
+smaller.  Merging has finished when the number of sectors holding data
+is zero, in other words <sectors_allocated> == <metadata_sectors>.
+
+Here is a practical example (using a hybrid of lvm and dmsetup commands):
+
+# lvs
+  LV      VG          Attr   LSize Origin  Snap%  Move Log Copy%  Convert
+  base    volumeGroup owi-a- 4.00g
+  snap    volumeGroup swi-a- 1.00g base  18.97
+
+# dmsetup status volumeGroup-snap
+0 8388608 snapshot 397896/2097152 1560
+                                  ^^^^ metadata sectors
+
+# lvconvert --merge -b volumeGroup/snap
+  Merging of volume snap started.
+
+# lvs volumeGroup/snap
+  LV      VG          Attr   LSize Origin  Snap%  Move Log Copy%  Convert
+  base    volumeGroup Owi-a- 4.00g          17.23
+
+# dmsetup status volumeGroup-base
+0 8388608 snapshot-merge 281688/2097152 1104
+
+# dmsetup status volumeGroup-base
+0 8388608 snapshot-merge 180480/2097152 712
+
+# dmsetup status volumeGroup-base
+0 8388608 snapshot-merge 16/2097152 16
+
+Merging has finished.
+
+# lvs
+  LV      VG          Attr   LSize Origin  Snap%  Move Log Copy%  Convert
+  base    volumeGroup owi-a- 4.00g
diff --git a/Documentation/fault-injection/provoke-crashes.txt b/Documentation/fault-injection/provoke-crashes.txt
new file mode 100644 (file)
index 0000000..7a9d3d8
--- /dev/null
@@ -0,0 +1,38 @@
+The lkdtm module provides an interface to crash or injure the kernel at
+predefined crashpoints to evaluate the reliability of crash dumps obtained
+using different dumping solutions. The module uses KPROBEs to instrument
+crashing points, but can also crash the kernel directly without KRPOBE
+support.
+
+
+You can provide the way either through module arguments when inserting
+the module, or through a debugfs interface.
+
+Usage: insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<>
+                               [cpoint_count={>0}]
+
+  recur_count : Recursion level for the stack overflow test. Default is 10.
+
+  cpoint_name : Crash point where the kernel is to be crashed. It can be
+        one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY,
+        FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD,
+        IDE_CORE_CP, DIRECT
+
+  cpoint_type : Indicates the action to be taken on hitting the crash point.
+     It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW,
+     CORRUPT_STACK, UNALIGNED_LOAD_STORE_WRITE, OVERWRITE_ALLOCATION,
+     WRITE_AFTER_FREE,
+
+  cpoint_count : Indicates the number of times the crash point is to be hit
+    to trigger an action. The default is 10.
+
+You can also induce failures by mounting debugfs and writing the type to
+<mountpoint>/provoke-crash/<crashpoint>. E.g.,
+
+  mount -t debugfs debugfs /mnt
+  echo EXCEPTION > /mnt/provoke-crash/INT_HARDWARE_ENTRY
+
+
+A special file is `DIRECT' which will induce the crash directly without
+KPROBE instrumentation. This mode is the only one available when the module
+is built on a kernel without KPROBEs support.
index 73ef30dbe61263e4697bba21dec15c31d649d2b1..a5cc0db63d7a8b104403099494f86db52edbdc79 100644 (file)
@@ -117,19 +117,25 @@ Who:      Mauro Carvalho Chehab <mchehab@infradead.org>
 ---------------------------
 
 What:  PCMCIA control ioctl (needed for pcmcia-cs [cardmgr, cardctl])
-When:  November 2005
+When:  2.6.35/2.6.36
 Files: drivers/pcmcia/: pcmcia_ioctl.c
 Why:   With the 16-bit PCMCIA subsystem now behaving (almost) like a
        normal hotpluggable bus, and with it using the default kernel
        infrastructure (hotplug, driver core, sysfs) keeping the PCMCIA
        control ioctl needed by cardmgr and cardctl from pcmcia-cs is
-       unnecessary, and makes further cleanups and integration of the
+       unnecessary and potentially harmful (it does not provide for
+       proper locking), and makes further cleanups and integration of the
        PCMCIA subsystem into the Linux kernel device driver model more
        difficult. The features provided by cardmgr and cardctl are either
        handled by the kernel itself now or are available in the new
        pcmciautils package available at
        http://kernel.org/pub/linux/utils/kernel/pcmcia/
-Who:   Dominik Brodowski <linux@brodo.de>
+
+       For all architectures except ARM, the associated config symbol
+       has been removed from kernel 2.6.34; for ARM, it will be likely
+       be removed from kernel 2.6.35. The actual code will then likely
+       be removed from kernel 2.6.36.
+Who:   Dominik Brodowski <linux@dominikbrodowski.net>
 
 ---------------------------
 
@@ -443,12 +449,6 @@ Who:       Alok N Kataria <akataria@vmware.com>
 
 ----------------------------
 
-What:  adt7473 hardware monitoring driver
-When:  February 2010
-Why:   Obsoleted by the adt7475 driver.
-Who:   Jean Delvare <khali@linux-fr.org>
-
----------------------------
 What:  Support for lcd_switch and display_get in asus-laptop driver
 When:  March 2010
 Why:   These two features use non-standard interfaces. There are the
@@ -550,3 +550,35 @@ Why:       udev fully replaces this special file system that only contains CAPI
        NCCI TTY device nodes. User space (pppdcapiplugin) works without
        noticing the difference.
 Who:   Jan Kiszka <jan.kiszka@web.de>
+
+----------------------------
+
+What:  KVM memory aliases support
+When:  July 2010
+Why:   Memory aliasing support is used for speeding up guest vga access
+       through the vga windows.
+
+       Modern userspace no longer uses this feature, so it's just bitrotted
+       code and can be removed with no impact.
+Who:   Avi Kivity <avi@redhat.com>
+
+----------------------------
+
+What:  KVM kernel-allocated memory slots
+When:  July 2010
+Why:   Since 2.6.25, kvm supports user-allocated memory slots, which are
+       much more flexible than kernel-allocated slots.  All current userspace
+       supports the newer interface and this code can be removed with no
+       impact.
+Who:   Avi Kivity <avi@redhat.com>
+
+----------------------------
+
+What:  KVM paravirt mmu host support
+When:  January 2011
+Why:   The paravirt mmu host support is slower than non-paravirt mmu, both
+       on newer and older hardware.  It is already not exposed to the guest,
+       and kept only for live migration purposes.
+Who:   Avi Kivity <avi@redhat.com>
+
+----------------------------
index 875d49696b6e6acf4386b8a1b71e797d3944da39..5139b8c9d5aff5fa08be9abcd98d3df08620152c 100644 (file)
@@ -62,6 +62,8 @@ jfs.txt
        - info and mount options for the JFS filesystem.
 locks.txt
        - info on file locking implementations, flock() vs. fcntl(), etc.
+logfs.txt
+       - info on the LogFS flash filesystem.
 mandatory-locking.txt
        - info on the Linux implementation of Sys V mandatory file locking.
 ncpfs.txt
index 18b9d0ca0630e281bb20dc5c5991e4b6f06b4643..06bbbed71206ff23ce3661a30f2ea08073cc1976 100644 (file)
@@ -460,13 +460,6 @@ in sys_read() and friends.
 
 --------------------------- dquot_operations -------------------------------
 prototypes:
-       int (*initialize) (struct inode *, int);
-       int (*drop) (struct inode *);
-       int (*alloc_space) (struct inode *, qsize_t, int);
-       int (*alloc_inode) (const struct inode *, unsigned long);
-       int (*free_space) (struct inode *, qsize_t);
-       int (*free_inode) (const struct inode *, unsigned long);
-       int (*transfer) (struct inode *, struct iattr *);
        int (*write_dquot) (struct dquot *);
        int (*acquire_dquot) (struct dquot *);
        int (*release_dquot) (struct dquot *);
@@ -479,13 +472,6 @@ a proper locking wrt the filesystem and call the generic quota operations.
 What filesystem should expect from the generic quota functions:
 
                FS recursion    Held locks when called
-initialize:    yes             maybe dqonoff_sem
-drop:          yes             -
-alloc_space:   ->mark_dirty()  -
-alloc_inode:   ->mark_dirty()  -
-free_space:    ->mark_dirty()  -
-free_inode:    ->mark_dirty()  -
-transfer:      yes             -
 write_dquot:   yes             dqonoff_sem or dqptr_sem
 acquire_dquot: yes             dqonoff_sem or dqptr_sem
 release_dquot: yes             dqonoff_sem or dqptr_sem
@@ -495,10 +481,6 @@ write_info:        yes             dqonoff_sem
 FS recursion means calling ->quota_read() and ->quota_write() from superblock
 operations.
 
-->alloc_space(), ->alloc_inode(), ->free_space(), ->free_inode() are called
-only directly by the filesystem and do not call any fs functions only
-the ->mark_dirty() operation.
-
 More details about quota locking can be found in fs/dquot.c.
 
 --------------------------- vm_operations_struct -----------------------------
diff --git a/Documentation/filesystems/logfs.txt b/Documentation/filesystems/logfs.txt
new file mode 100644 (file)
index 0000000..e64c94b
--- /dev/null
@@ -0,0 +1,241 @@
+
+The LogFS Flash Filesystem
+==========================
+
+Specification
+=============
+
+Superblocks
+-----------
+
+Two superblocks exist at the beginning and end of the filesystem.
+Each superblock is 256 Bytes large, with another 3840 Bytes reserved
+for future purposes, making a total of 4096 Bytes.
+
+Superblock locations may differ for MTD and block devices.  On MTD the
+first non-bad block contains a superblock in the first 4096 Bytes and
+the last non-bad block contains a superblock in the last 4096 Bytes.
+On block devices, the first 4096 Bytes of the device contain the first
+superblock and the last aligned 4096 Byte-block contains the second
+superblock.
+
+For the most part, the superblocks can be considered read-only.  They
+are written only to correct errors detected within the superblocks,
+move the journal and change the filesystem parameters through tunefs.
+As a result, the superblock does not contain any fields that require
+constant updates, like the amount of free space, etc.
+
+Segments
+--------
+
+The space in the device is split up into equal-sized segments.
+Segments are the primary write unit of LogFS.  Within each segments,
+writes happen from front (low addresses) to back (high addresses.  If
+only a partial segment has been written, the segment number, the
+current position within and optionally a write buffer are stored in
+the journal.
+
+Segments are erased as a whole.  Therefore Garbage Collection may be
+required to completely free a segment before doing so.
+
+Journal
+--------
+
+The journal contains all global information about the filesystem that
+is subject to frequent change.  At mount time, it has to be scanned
+for the most recent commit entry, which contains a list of pointers to
+all currently valid entries.
+
+Object Store
+------------
+
+All space except for the superblocks and journal is part of the object
+store.  Each segment contains a segment header and a number of
+objects, each consisting of the object header and the payload.
+Objects are either inodes, directory entries (dentries), file data
+blocks or indirect blocks.
+
+Levels
+------
+
+Garbage collection (GC) may fail if all data is written
+indiscriminately.  One requirement of GC is that data is seperated
+roughly according to the distance between the tree root and the data.
+Effectively that means all file data is on level 0, indirect blocks
+are on levels 1, 2, 3 4 or 5 for 1x, 2x, 3x, 4x or 5x indirect blocks,
+respectively.  Inode file data is on level 6 for the inodes and 7-11
+for indirect blocks.
+
+Each segment contains objects of a single level only.  As a result,
+each level requires its own seperate segment to be open for writing.
+
+Inode File
+----------
+
+All inodes are stored in a special file, the inode file.  Single
+exception is the inode file's inode (master inode) which for obvious
+reasons is stored in the journal instead.  Instead of data blocks, the
+leaf nodes of the inode files are inodes.
+
+Aliases
+-------
+
+Writes in LogFS are done by means of a wandering tree.  A naïve
+implementation would require that for each write or a block, all
+parent blocks are written as well, since the block pointers have
+changed.  Such an implementation would not be very efficient.
+
+In LogFS, the block pointer changes are cached in the journal by means
+of alias entries.  Each alias consists of its logical address - inode
+number, block index, level and child number (index into block) - and
+the changed data.  Any 8-byte word can be changes in this manner.
+
+Currently aliases are used for block pointers, file size, file used
+bytes and the height of an inodes indirect tree.
+
+Segment Aliases
+---------------
+
+Related to regular aliases, these are used to handle bad blocks.
+Initially, bad blocks are handled by moving the affected segment
+content to a spare segment and noting this move in the journal with a
+segment alias, a simple (to, from) tupel.  GC will later empty this
+segment and the alias can be removed again.  This is used on MTD only.
+
+Vim
+---
+
+By cleverly predicting the life time of data, it is possible to
+seperate long-living data from short-living data and thereby reduce
+the GC overhead later.  Each type of distinc life expectency (vim) can
+have a seperate segment open for writing.  Each (level, vim) tupel can
+be open just once.  If an open segment with unknown vim is encountered
+at mount time, it is closed and ignored henceforth.
+
+Indirect Tree
+-------------
+
+Inodes in LogFS are similar to FFS-style filesystems with direct and
+indirect block pointers.  One difference is that LogFS uses a single
+indirect pointer that can be either a 1x, 2x, etc. indirect pointer.
+A height field in the inode defines the height of the indirect tree
+and thereby the indirection of the pointer.
+
+Another difference is the addressing of indirect blocks.  In LogFS,
+the first 16 pointers in the first indirect block are left empty,
+corresponding to the 16 direct pointers in the inode.  In ext2 (maybe
+others as well) the first pointer in the first indirect block
+corresponds to logical block 12, skipping the 12 direct pointers.
+So where ext2 is using arithmetic to better utilize space, LogFS keeps
+arithmetic simple and uses compression to save space.
+
+Compression
+-----------
+
+Both file data and metadata can be compressed.  Compression for file
+data can be enabled with chattr +c and disabled with chattr -c.  Doing
+so has no effect on existing data, but new data will be stored
+accordingly.  New inodes will inherit the compression flag of the
+parent directory.
+
+Metadata is always compressed.  However, the space accounting ignores
+this and charges for the uncompressed size.  Failing to do so could
+result in GC failures when, after moving some data, indirect blocks
+compress worse than previously.  Even on a 100% full medium, GC may
+not consume any extra space, so the compression gains are lost space
+to the user.
+
+However, they are not lost space to the filesystem internals.  By
+cheating the user for those bytes, the filesystem gained some slack
+space and GC will run less often and faster.
+
+Garbage Collection and Wear Leveling
+------------------------------------
+
+Garbage collection is invoked whenever the number of free segments
+falls below a threshold.  The best (known) candidate is picked based
+on the least amount of valid data contained in the segment.  All
+remaining valid data is copied elsewhere, thereby invalidating it.
+
+The GC code also checks for aliases and writes then back if their
+number gets too large.
+
+Wear leveling is done by occasionally picking a suboptimal segment for
+garbage collection.  If a stale segments erase count is significantly
+lower than the active segments' erase counts, it will be picked.  Wear
+leveling is rate limited, so it will never monopolize the device for
+more than one segment worth at a time.
+
+Values for "occasionally", "significantly lower" are compile time
+constants.
+
+Hashed directories
+------------------
+
+To satisfy efficient lookup(), directory entries are hashed and
+located based on the hash.  In order to both support large directories
+and not be overly inefficient for small directories, several hash
+tables of increasing size are used.  For each table, the hash value
+modulo the table size gives the table index.
+
+Tables sizes are chosen to limit the number of indirect blocks with a
+fully populated table to 0, 1, 2 or 3 respectively.  So the first
+table contains 16 entries, the second 512-16, etc.
+
+The last table is special in several ways.  First its size depends on
+the effective 32bit limit on telldir/seekdir cookies.  Since logfs
+uses the upper half of the address space for indirect blocks, the size
+is limited to 2^31.  Secondly the table contains hash buckets with 16
+entries each.
+
+Using single-entry buckets would result in birthday "attacks".  At
+just 2^16 used entries, hash collisions would be likely (P >= 0.5).
+My math skills are insufficient to do the combinatorics for the 17x
+collisions necessary to overflow a bucket, but testing showed that in
+10,000 runs the lowest directory fill before a bucket overflow was
+188,057,130 entries with an average of 315,149,915 entries.  So for
+directory sizes of up to a million, bucket overflows should be
+virtually impossible under normal circumstances.
+
+With carefully chosen filenames, it is obviously possible to cause an
+overflow with just 21 entries (4 higher tables + 16 entries + 1).  So
+there may be a security concern if a malicious user has write access
+to a directory.
+
+Open For Discussion
+===================
+
+Device Address Space
+--------------------
+
+A device address space is used for caching.  Both block devices and
+MTD provide functions to either read a single page or write a segment.
+Partial segments may be written for data integrity, but where possible
+complete segments are written for performance on simple block device
+flash media.
+
+Meta Inodes
+-----------
+
+Inodes are stored in the inode file, which is just a regular file for
+most purposes.  At umount time, however, the inode file needs to
+remain open until all dirty inodes are written.  So
+generic_shutdown_super() may not close this inode, but shouldn't
+complain about remaining inodes due to the inode file either.  Same
+goes for mapping inode of the device address space.
+
+Currently logfs uses a hack that essentially copies part of fs/inode.c
+code over.  A general solution would be preferred.
+
+Indirect block mapping
+----------------------
+
+With compression, the block device (or mapping inode) cannot be used
+to cache indirect blocks.  Some other place is required.  Currently
+logfs uses the top half of each inode's address space.  The low 8TB
+(on 32bit) are filled with file data, the high 8TB are used for
+indirect blocks.
+
+One problem is that 16TB files created on 64bit systems actually have
+data in the top 8TB.  But files >16TB would cause problems anyway, so
+only the limit has changed.
index 1bd0d0c05171efed3c12863300a71e3bc4f36bd8..6a53a84afc721b1a6f1ff9736f7e2e8821fb3cec 100644 (file)
@@ -17,8 +17,7 @@ kernels must turn 4.1 on or off *before* turning support for version 4
 on or off; rpc.nfsd does this correctly.)
 
 The NFSv4 minorversion 1 (NFSv4.1) implementation in nfsd is based
-on the latest NFSv4.1 Internet Draft:
-http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-29
+on RFC 5661.
 
 From the many new features in NFSv4.1 the current implementation
 focuses on the mandatory-to-implement NFSv4.1 Sessions, providing
@@ -44,7 +43,7 @@ interoperability problems with future clients.  Known issues:
          trunking, but this is a mandatory feature, and its use is
          recommended to clients in a number of places.  (E.g. to ensure
          timely renewal in case an existing connection's retry timeouts
-         have gotten too long; see section 8.3 of the draft.)
+         have gotten too long; see section 8.3 of the RFC.)
          Therefore, lack of this feature may cause future clients to
          fail.
        - Incomplete backchannel support: incomplete backchannel gss
index 0d07513a67a661440fc1599121bb1dc38560bc33..96a44dd95e03f2a856a9fdd23cb9afbee76804b2 100644 (file)
@@ -164,6 +164,7 @@ read the file /proc/PID/status:
   VmExe:        68 kB
   VmLib:      1412 kB
   VmPTE:        20 kb
+  VmSwap:        0 kB
   Threads:        1
   SigQ:   0/28578
   SigPnd: 0000000000000000
@@ -188,6 +189,12 @@ memory usage. Its seven fields are explained in Table 1-3.  The stat file
 contains details information about the process itself.  Its fields are
 explained in Table 1-4.
 
+(for SMP CONFIG users)
+For making accounting scalable, RSS related information are handled in
+asynchronous manner and the vaule may not be very precise. To see a precise
+snapshot of a moment, you can see /proc/<pid>/smaps file and scan page table.
+It's slow but very precise.
+
 Table 1-2: Contents of the statm files (as of 2.6.30-rc7)
 ..............................................................................
  Field                       Content
@@ -213,6 +220,7 @@ Table 1-2: Contents of the statm files (as of 2.6.30-rc7)
  VmExe                       size of text segment
  VmLib                       size of shared library code
  VmPTE                       size of page table entries
+ VmSwap                      size of swap usage (the number of referred swapents)
  Threads                     number of threads
  SigQ                        number of signals queued/max. number for queue
  SigPnd                      bitmap of pending signals for the thread
@@ -430,6 +438,7 @@ Table 1-5: Kernel info in /proc
  modules     List of loaded modules                            
  mounts      Mounted filesystems                               
  net         Networking info (see text)                        
+ pagetypeinfo Additional page allocator information (see text)  (2.5)
  partitions  Table of partitions known to the system           
  pci        Deprecated info of PCI bus (new way -> /proc/bus/pci/,
              decoupled by lspci                                        (2.4)
@@ -584,7 +593,7 @@ Node 0, zone      DMA      0      4      5      4      4      3 ...
 Node 0, zone   Normal      1      0      0      1    101      8 ...
 Node 0, zone  HighMem      2      0      0      1      1      0 ...
 
-Memory fragmentation is a problem under some workloads, and buddyinfo is a 
+External fragmentation is a problem under some workloads, and buddyinfo is a
 useful tool for helping diagnose these problems.  Buddyinfo will give you a 
 clue as to how big an area you can safely allocate, or why a previous
 allocation failed.
@@ -594,6 +603,48 @@ available.  In this case, there are 0 chunks of 2^0*PAGE_SIZE available in
 ZONE_DMA, 4 chunks of 2^1*PAGE_SIZE in ZONE_DMA, 101 chunks of 2^4*PAGE_SIZE 
 available in ZONE_NORMAL, etc... 
 
+More information relevant to external fragmentation can be found in
+pagetypeinfo.
+
+> cat /proc/pagetypeinfo
+Page block order: 9
+Pages per block:  512
+
+Free pages count per migrate type at order       0      1      2      3      4      5      6      7      8      9     10
+Node    0, zone      DMA, type    Unmovable      0      0      0      1      1      1      1      1      1      1      0
+Node    0, zone      DMA, type  Reclaimable      0      0      0      0      0      0      0      0      0      0      0
+Node    0, zone      DMA, type      Movable      1      1      2      1      2      1      1      0      1      0      2
+Node    0, zone      DMA, type      Reserve      0      0      0      0      0      0      0      0      0      1      0
+Node    0, zone      DMA, type      Isolate      0      0      0      0      0      0      0      0      0      0      0
+Node    0, zone    DMA32, type    Unmovable    103     54     77      1      1      1     11      8      7      1      9
+Node    0, zone    DMA32, type  Reclaimable      0      0      2      1      0      0      0      0      1      0      0
+Node    0, zone    DMA32, type      Movable    169    152    113     91     77     54     39     13      6      1    452
+Node    0, zone    DMA32, type      Reserve      1      2      2      2      2      0      1      1      1      1      0
+Node    0, zone    DMA32, type      Isolate      0      0      0      0      0      0      0      0      0      0      0
+
+Number of blocks type     Unmovable  Reclaimable      Movable      Reserve      Isolate
+Node 0, zone      DMA            2            0            5            1            0
+Node 0, zone    DMA32           41            6          967            2            0
+
+Fragmentation avoidance in the kernel works by grouping pages of different
+migrate types into the same contiguous regions of memory called page blocks.
+A page block is typically the size of the default hugepage size e.g. 2MB on
+X86-64. By keeping pages grouped based on their ability to move, the kernel
+can reclaim pages within a page block to satisfy a high-order allocation.
+
+The pagetypinfo begins with information on the size of a page block. It
+then gives the same type of information as buddyinfo except broken down
+by migrate-type and finishes with details on how many page blocks of each
+type exist.
+
+If min_free_kbytes has been tuned correctly (recommendations made by hugeadm
+from libhugetlbfs http://sourceforge.net/projects/libhugetlbfs/), one can
+make an estimate of the likely number of huge pages that can be allocated
+at a given point in time. All the "Movable" blocks should be allocatable
+unless memory has been mlock()'d. Some of the Reclaimable blocks should
+also be allocatable although a lot of filesystem metadata may have to be
+reclaimed to achieve this.
+
 ..............................................................................
 
 meminfo:
index 23a181074f94b5f65ac25c603d65fc749fa5ed78..fc0e39af43c32a42343f97daed5a5d285f844863 100644 (file)
@@ -837,6 +837,9 @@ replicas continue to be exactly same.
         individual lists does not affect propagation or the way propagation
         tree is modified by operations.
 
+       All vfsmounts in a peer group have the same ->mnt_master.  If it is
+       non-NULL, they form a contiguous (ordered) segment of slave list.
+
        A example propagation tree looks as shown in the figure below.
        [ NOTE: Though it looks like a forest, if we consider all the shared
        mounts as a conceptual entity called 'pnode', it becomes a tree]
@@ -874,8 +877,19 @@ replicas continue to be exactly same.
 
        NOTE: The propagation tree is orthogonal to the mount tree.
 
+8B Locking:
+
+       ->mnt_share, ->mnt_slave, ->mnt_slave_list, ->mnt_master are protected
+       by namespace_sem (exclusive for modifications, shared for reading).
+
+       Normally we have ->mnt_flags modifications serialized by vfsmount_lock.
+       There are two exceptions: do_add_mount() and clone_mnt().
+       The former modifies a vfsmount that has not been visible in any shared
+       data structures yet.
+       The latter holds namespace_sem and the only references to vfsmount
+       are in lists that can't be traversed without namespace_sem.
 
-8B Algorithm:
+8C Algorithm:
 
        The crux of the implementation resides in rbind/move operation.
 
index 1866c27eec693e7e88f7420e3ac895079cba02d9..c2c6e9b39bbe39e747fbfe7ffed869bff9cdfff7 100644 (file)
@@ -253,6 +253,70 @@ pin setup (e.g. controlling which pin the GPIO uses, pullup/pulldown).
 Also note that it's your responsibility to have stopped using a GPIO
 before you free it.
 
+Considering in most cases GPIOs are actually configured right after they
+are claimed, three additional calls are defined:
+
+       /* request a single GPIO, with initial configuration specified by
+        * 'flags', identical to gpio_request() wrt other arguments and
+        * return value
+        */
+       int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
+
+       /* request multiple GPIOs in a single call
+        */
+       int gpio_request_array(struct gpio *array, size_t num);
+
+       /* release multiple GPIOs in a single call
+        */
+       void gpio_free_array(struct gpio *array, size_t num);
+
+where 'flags' is currently defined to specify the following properties:
+
+       * GPIOF_DIR_IN          - to configure direction as input
+       * GPIOF_DIR_OUT         - to configure direction as output
+
+       * GPIOF_INIT_LOW        - as output, set initial level to LOW
+       * GPIOF_INIT_HIGH       - as output, set initial level to HIGH
+
+since GPIOF_INIT_* are only valid when configured as output, so group valid
+combinations as:
+
+       * GPIOF_IN              - configure as input
+       * GPIOF_OUT_INIT_LOW    - configured as output, initial level LOW
+       * GPIOF_OUT_INIT_HIGH   - configured as output, initial level HIGH
+
+In the future, these flags can be extended to support more properties such
+as open-drain status.
+
+Further more, to ease the claim/release of multiple GPIOs, 'struct gpio' is
+introduced to encapsulate all three fields as:
+
+       struct gpio {
+               unsigned        gpio;
+               unsigned long   flags;
+               const char      *label;
+       };
+
+A typical example of usage:
+
+       static struct gpio leds_gpios[] = {
+               { 32, GPIOF_OUT_INIT_HIGH, "Power LED" }, /* default to ON */
+               { 33, GPIOF_OUT_INIT_LOW,  "Green LED" }, /* default to OFF */
+               { 34, GPIOF_OUT_INIT_LOW,  "Red LED"   }, /* default to OFF */
+               { 35, GPIOF_OUT_INIT_LOW,  "Blue LED"  }, /* default to OFF */
+               { ... },
+       };
+
+       err = gpio_request_one(31, GPIOF_IN, "Reset Button");
+       if (err)
+               ...
+
+       err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
+       if (err)
+               ...
+
+       gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));
+
 
 GPIOs mapped to IRQs
 --------------------
diff --git a/Documentation/hwmon/adt7411 b/Documentation/hwmon/adt7411
new file mode 100644 (file)
index 0000000..1632960
--- /dev/null
@@ -0,0 +1,42 @@
+Kernel driver adt7411
+=====================
+
+Supported chips:
+  * Analog Devices ADT7411
+    Prefix: 'adt7411'
+    Addresses scanned: 0x48, 0x4a, 0x4b
+    Datasheet: Publicly available at the Analog Devices website
+
+Author: Wolfram Sang (based on adt7470 by Darrick J. Wong)
+
+Description
+-----------
+
+This driver implements support for the Analog Devices ADT7411 chip. There may
+be other chips that implement this interface.
+
+The ADT7411 can use an I2C/SMBus compatible 2-wire interface or an
+SPI-compatible 4-wire interface. It provides a 10-bit analog to digital
+converter which measures 1 temperature, vdd and 8 input voltages. It has an
+internal temperature sensor, but an external one can also be connected (one
+loses 2 inputs then). There are high- and low-limit registers for all inputs.
+
+Check the datasheet for details.
+
+sysfs-Interface
+---------------
+
+in0_input      - vdd voltage input
+in[1-8]_input  - analog 1-8 input
+temp1_input    - temperature input
+
+Besides standard interfaces, this driver adds (0 = off, 1 = on):
+
+  adc_ref_vdd  - Use vdd as reference instead of 2.25 V
+  fast_sampling        - Sample at 22.5 kHz instead of 1.4 kHz, but drop filters
+  no_average   - Turn off averaging over 16 samples
+
+Notes
+-----
+
+SPI, external temperature sensor and limit registers are not supported yet.
diff --git a/Documentation/hwmon/adt7473 b/Documentation/hwmon/adt7473
deleted file mode 100644 (file)
index 446612b..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-Kernel driver adt7473
-======================
-
-Supported chips:
-  * Analog Devices ADT7473
-    Prefix: 'adt7473'
-    Addresses scanned: I2C 0x2C, 0x2D, 0x2E
-    Datasheet: Publicly available at the Analog Devices website
-
-Author: Darrick J. Wong
-
-This driver is depreacted, please use the adt7475 driver instead.
-
-Description
------------
-
-This driver implements support for the Analog Devices ADT7473 chip family.
-
-The ADT7473 uses the 2-wire interface compatible with the SMBUS 2.0
-specification. Using an analog to digital converter it measures three (3)
-temperatures and two (2) voltages. It has four (4) 16-bit counters for
-measuring fan speed. There are three (3) PWM outputs that can be used
-to control fan speed.
-
-A sophisticated control system for the PWM outputs is designed into the
-ADT7473 that allows fan speed to be adjusted automatically based on any of the
-three temperature sensors. Each PWM output is individually adjustable and
-programmable. Once configured, the ADT7473 will adjust the PWM outputs in
-response to the measured temperatures without further host intervention.
-This feature can also be disabled for manual control of the PWM's.
-
-Each of the measured inputs (voltage, temperature, fan speed) has
-corresponding high/low limit values. The ADT7473 will signal an ALARM if
-any measured value exceeds either limit.
-
-The ADT7473 samples all inputs continuously. The driver will not read
-the registers more often than once every other second. Further,
-configuration data is only read once per minute.
-
-Special Features
-----------------
-
-The ADT7473 have a 10-bit ADC and can therefore measure temperatures
-with 0.25 degC resolution. Temperature readings can be configured either
-for twos complement format or "Offset 64" format, wherein 63 is subtracted
-from the raw value to get the temperature value.
-
-The Analog Devices datasheet is very detailed and describes a procedure for
-determining an optimal configuration for the automatic PWM control.
-
-Configuration Notes
--------------------
-
-Besides standard interfaces driver adds the following:
-
-* PWM Control
-
-* pwm#_auto_point1_pwm and temp#_auto_point1_temp and
-* pwm#_auto_point2_pwm and temp#_auto_point2_temp -
-
-point1: Set the pwm speed at a lower temperature bound.
-point2: Set the pwm speed at a higher temperature bound.
-
-The ADT7473 will scale the pwm between the lower and higher pwm speed when
-the temperature is between the two temperature boundaries.  PWM values range
-from 0 (off) to 255 (full speed).  Fan speed will be set to maximum when the
-temperature sensor associated with the PWM control exceeds temp#_max.
-
-Notes
------
-
-The NVIDIA binary driver presents an ADT7473 chip via an on-card i2c bus.
-Unfortunately, they fail to set the i2c adapter class, so this driver may
-fail to find the chip until the nvidia driver is patched.
diff --git a/Documentation/hwmon/asc7621 b/Documentation/hwmon/asc7621
new file mode 100644 (file)
index 0000000..7287be7
--- /dev/null
@@ -0,0 +1,296 @@
+Kernel driver asc7621
+==================
+
+Supported chips:
+    Andigilog aSC7621 and aSC7621a
+    Prefix: 'asc7621'
+    Addresses scanned: I2C 0x2c, 0x2d, 0x2e
+    Datasheet: http://www.fairview5.com/linux/asc7621/asc7621.pdf
+
+Author:
+               George Joseph
+
+Description provided by Dave Pivin @ Andigilog:
+
+Andigilog has both the PECI and pre-PECI versions of the Heceta-6, as
+Intel calls them. Heceta-6e has high frequency PWM and Heceta-6p has
+added PECI and a 4th thermal zone. The Andigilog aSC7611 is the
+Heceta-6e part and aSC7621 is the Heceta-6p part. They are both in
+volume production, shipping to Intel and their subs.
+
+We have enhanced both parts relative to the governing Intel
+specification. First enhancement is temperature reading resolution. We
+have used registers below 20h for vendor-specific functions in addition
+to those in the Intel-specified vendor range.
+
+Our conversion process produces a result that is reported as two bytes.
+The fan speed control uses this finer value to produce a "step-less" fan
+PWM output. These two bytes are "read-locked" to guarantee that once a
+high or low byte is read, the other byte is locked-in until after the
+next read of any register. So to get an atomic reading, read high or low
+byte, then the very next read should be the opposite byte. Our data
+sheet says 10-bits of resolution, although you may find the lower bits
+are active, they are not necessarily reliable or useful externally. We
+chose not to mask them.
+
+We employ significant filtering that is user tunable as described in the
+data sheet. Our temperature reports and fan PWM outputs are very smooth
+when compared to the competition, in addition to the higher resolution
+temperature reports. The smoother PWM output does not require user
+intervention.
+
+We offer GPIO features on the former VID pins. These are open-drain
+outputs or inputs and may be used as general purpose I/O or as alarm
+outputs that are based on temperature limits. These are in 19h and 1Ah.
+
+We offer flexible mapping of temperature readings to thermal zones. Any
+temperature may be mapped to any zone, which has a default assignment
+that follows Intel's specs.
+
+Since there is a fan to zone assignment that allows for the "hotter" of
+a set of zones to control the PWM of an individual fan, but there is no
+indication to the user, we have added an indicator that shows which zone
+is currently controlling the PWM for a given fan. This is in register
+00h.
+
+Both remote diode temperature readings may be given an offset value such
+that the reported reading as well as the temperature used to determine
+PWM may be offset for system calibration purposes.
+
+PECI Extended configuration allows for having more than two domains per
+PECI address and also provides an enabling function for each PECI
+address. One could use our flexible zone assignment to have a zone
+assigned to up to 4 PECI addresses. This is not possible in the default
+Intel configuration. This would be useful in multi-CPU systems with
+individual fans on each that would benefit from individual fan control.
+This is in register 0Eh.
+
+The tachometer measurement system is flexible and able to adapt to many
+fan types. We can also support pulse-stretched PWM so that 3-wire fans
+may be used. These characteristics are in registers 04h to 07h.
+
+Finally, we have added a tach disable function that turns off the tach
+measurement system for individual tachs in order to save power. That is
+in register 75h.
+
+--
+aSC7621 Product Description
+
+The aSC7621 has a two wire digital interface compatible with SMBus 2.0.
+Using a 10-bit ADC, the aSC7621 measures the temperature of two remote diode
+connected transistors as well as its own die. Support for Platform
+Environmental Control Interface (PECI) is included.
+
+Using temperature information from these four zones, an automatic fan speed
+control algorithm is employed to minimize acoustic impact while achieving
+recommended CPU temperature under varying operational loads.
+
+To set fan speed, the aSC7621 has three independent pulse width modulation
+(PWM) outputs that are controlled by one, or a combination of three,
+temperature zones. Both high- and low-frequency PWM ranges are supported.
+
+The aSC7621 also includes a digital filter that can be invoked to smooth
+temperature readings for better control of fan speed and minimum acoustic
+impact.
+
+The aSC7621 has tachometer inputs to measure fan speed on up to four fans.
+Limit and status registers for all measured values are included to alert
+the system host that any measurements are outside of programmed limits
+via status registers.
+
+System voltages of VCCP, 2.5V, 3.3V, 5.0V, and 12V motherboard power are
+monitored efficiently with internal scaling resistors.
+
+Features
+- Supports PECI interface and monitors internal and remote thermal diodes
+- 2-wire, SMBus 2.0 compliant, serial interface
+- 10-bit ADC
+- Monitors VCCP, 2.5V, 3.3V, 5.0V, and 12V motherboard/processor supplies
+- Programmable autonomous fan control based on temperature readings
+- Noise filtering of temperature reading for fan speed control
+- 0.25C digital temperature sensor resolution
+- 3 PWM fan speed control outputs for 2-, 3- or 4-wire fans and up to 4 fan
+       tachometer inputs
+- Enhanced measured temperature to Temperature Zone assignment.
+- Provides high and low PWM frequency ranges
+- 3 GPIO pins for custom use
+- 24-Lead QSOP package
+
+Configuration Notes
+===================
+
+Except where noted below, the sysfs entries created by this driver follow
+the standards defined in "sysfs-interface".
+
+temp1_source
+       0       (default) peci_legacy = 0, Remote 1 Temperature
+                       peci_legacy = 1, PECI Processor Temperature 0
+       1       Remote 1 Temperature
+       2       Remote 2 Temperature
+       3       Internal Temperature
+       4       PECI Processor Temperature 0
+       5       PECI Processor Temperature 1
+       6       PECI Processor Temperature 2
+       7  PECI Processor Temperature 3
+
+temp2_source
+       0       (default) Internal Temperature
+       1       Remote 1 Temperature
+       2       Remote 2 Temperature
+       3       Internal Temperature
+       4       PECI Processor Temperature 0
+       5       PECI Processor Temperature 1
+       6       PECI Processor Temperature 2
+       7       PECI Processor Temperature 3
+
+temp3_source
+       0       (default) Remote 2 Temperature
+       1       Remote 1 Temperature
+       2       Remote 2 Temperature
+       3       Internal Temperature
+       4       PECI Processor Temperature 0
+       5       PECI Processor Temperature 1
+       6       PECI Processor Temperature 2
+       7       PECI Processor Temperature 3
+
+temp4_source
+       0       (default) peci_legacy = 0, PECI Processor Temperature 0
+                       peci_legacy = 1, Remote 1 Temperature
+       1       Remote 1 Temperature
+       2       Remote 2 Temperature
+       3       Internal Temperature
+       4       PECI Processor Temperature 0
+       5       PECI Processor Temperature 1
+       6       PECI Processor Temperature 2
+       7       PECI Processor Temperature 3
+
+temp[1-4]_smoothing_enable
+temp[1-4]_smoothing_time
+       Smooths spikes in temp readings caused by noise.
+       Valid values in milliseconds are:
+       35000
+       17600
+       11800
+        7000
+        4400
+        3000
+        1600
+         800
+
+temp[1-4]_crit
+       When the corresponding zone temperature reaches this value,
+       ALL pwm outputs will got to 100%.
+
+temp[5-8]_input
+temp[5-8]_enable
+       The aSC7621 can also read temperatures provided by the processor
+       via the PECI bus.  Usually these are "core" temps and are relative
+       to the point where the automatic thermal control circuit starts
+       throttling.  This means that these are usually negative numbers.
+
+pwm[1-3]_enable
+       0               Fan off.
+       1               Fan on manual control.
+       2               Fan on automatic control and will run at the minimum pwm
+                               if the temperature for the zone is below the minimum.
+       3               Fan on automatic control but will be off if the temperature
+                               for the zone is below the minimum.
+       4-254   Ignored.
+       255             Fan on full.
+
+pwm[1-3]_auto_channels
+       Bitmap as described in sysctl-interface with the following
+       exceptions...
+       Only the following combination of zones (and their corresponding masks)
+       are valid:
+       1
+       2
+       3
+       2,3
+       1,2,3
+       4
+       1,2,3,4
+
+       Special values:
+       0                       Disabled.
+       16              Fan on manual control.
+       31              Fan on full.
+
+
+pwm[1-3]_invert
+       When set, inverts the meaning of pwm[1-3].
+       i.e.  when pwm = 0, the fan will be on full and
+       when pwm = 255 the fan will be off.
+
+pwm[1-3]_freq
+       PWM frequency in Hz
+       Valid values in Hz are:
+
+       10
+       15
+       23
+       30  (default)
+       38
+       47
+       62
+       94
+       23000
+       24000
+       25000
+       26000
+       27000
+       28000
+       29000
+       30000
+
+       Setting any other value will be ignored.
+
+peci_enable
+       Enables or disables PECI
+
+peci_avg
+       Input filter average time.
+
+       0       0 Sec. (no Smoothing) (default)
+       1       0.25 Sec.
+       2       0.5 Sec.
+       3       1.0 Sec.
+       4       2.0 Sec.
+       5       4.0 Sec.
+       6       8.0 Sec.
+       7       0.0 Sec.
+
+peci_legacy
+
+       0       Standard Mode (default)
+               Remote Diode 1 reading is associated with
+               Temperature Zone 1, PECI is associated with
+               Zone 4
+
+       1       Legacy Mode
+               PECI is associated with Temperature Zone 1,
+               Remote Diode 1 is associated with Zone 4
+
+peci_diode
+       Diode filter
+
+       0       0.25 Sec.
+       1       1.1 Sec.
+       2       2.4 Sec.  (default)
+       3       3.4 Sec.
+       4       5.0 Sec.
+       5       6.8 Sec.
+       6       10.2 Sec.
+       7       16.4 Sec.
+
+peci_4domain
+       Four domain enable
+
+       0       1 or 2 Domains for enabled processors (default)
+       1       3 or 4 Domains for enabled processors
+
+peci_domain
+       Domain
+
+       0       Processor contains a single domain (0)   (default)
+       1       Processor contains two domains (0,1)
index f9ba96c0ac4acf19573ca552703bd7014b350488..8d08bf0d38edec17566cd5cc3848bd825a3f8636 100644 (file)
@@ -5,31 +5,23 @@ Supported chips:
   * IT8705F
     Prefix: 'it87'
     Addresses scanned: from Super I/O config space (8 I/O ports)
-    Datasheet: Publicly available at the ITE website
-               http://www.ite.com.tw/product_info/file/pc/IT8705F_V.0.4.1.pdf
+    Datasheet: Once publicly available at the ITE website, but no longer
   * IT8712F
     Prefix: 'it8712'
     Addresses scanned: from Super I/O config space (8 I/O ports)
-    Datasheet: Publicly available at the ITE website
-               http://www.ite.com.tw/product_info/file/pc/IT8712F_V0.9.1.pdf
-               http://www.ite.com.tw/product_info/file/pc/Errata%20V0.1%20for%20IT8712F%20V0.9.1.pdf
-               http://www.ite.com.tw/product_info/file/pc/IT8712F_V0.9.3.pdf
+    Datasheet: Once publicly available at the ITE website, but no longer
   * IT8716F/IT8726F
     Prefix: 'it8716'
     Addresses scanned: from Super I/O config space (8 I/O ports)
-    Datasheet: Publicly available at the ITE website
-               http://www.ite.com.tw/product_info/file/pc/IT8716F_V0.3.ZIP
-               http://www.ite.com.tw/product_info/file/pc/IT8726F_V0.3.pdf
+    Datasheet: Once publicly available at the ITE website, but no longer
   * IT8718F
     Prefix: 'it8718'
     Addresses scanned: from Super I/O config space (8 I/O ports)
-    Datasheet: Publicly available at the ITE website
-               http://www.ite.com.tw/product_info/file/pc/IT8718F_V0.2.zip
-               http://www.ite.com.tw/product_info/file/pc/IT8718F_V0%203_(for%20C%20version).zip
+    Datasheet: Once publicly available at the ITE website, but no longer
   * IT8720F
     Prefix: 'it8720'
     Addresses scanned: from Super I/O config space (8 I/O ports)
-    Datasheet: Not yet publicly available.
+    Datasheet: Not publicly available
   * SiS950   [clone of IT8705F]
     Prefix: 'it87'
     Addresses scanned: from Super I/O config space (8 I/O ports)
@@ -136,6 +128,10 @@ registers are read whenever any data is read (unless it is less than 1.5
 seconds since the last update). This means that you can easily miss
 once-only alarms.
 
+Out-of-limit readings can also result in beeping, if the chip is properly
+wired and configured. Beeping can be enabled or disabled per sensor type
+(temperatures, voltages and fans.)
+
 The IT87xx only updates its values each 1.5 seconds; reading it more often
 will do no harm, but will return 'old' values.
 
@@ -150,11 +146,38 @@ Fan speed control
 -----------------
 
 The fan speed control features are limited to manual PWM mode. Automatic
-"Smart Guardian" mode control handling is not implemented. However
-if you want to go for "manual mode" just write 1 to pwmN_enable.
+"Smart Guardian" mode control handling is only implemented for older chips
+(see below.) However if you want to go for "manual mode" just write 1 to
+pwmN_enable.
 
 If you are only able to control the fan speed with very small PWM values,
 try lowering the PWM base frequency (pwm1_freq). Depending on the fan,
 it may give you a somewhat greater control range. The same frequency is
 used to drive all fan outputs, which is why pwm2_freq and pwm3_freq are
 read-only.
+
+
+Automatic fan speed control (old interface)
+-------------------------------------------
+
+The driver supports the old interface to automatic fan speed control
+which is implemented by IT8705F chips up to revision F and IT8712F
+chips up to revision G.
+
+This interface implements 4 temperature vs. PWM output trip points.
+The PWM output of trip point 4 is always the maximum value (fan running
+at full speed) while the PWM output of the other 3 trip points can be
+freely chosen. The temperature of all 4 trip points can be freely chosen.
+Additionally, trip point 1 has an hysteresis temperature attached, to
+prevent fast switching between fan on and off.
+
+The chip automatically computes the PWM output value based on the input
+temperature, based on this simple rule: if the temperature value is
+between trip point N and trip point N+1 then the PWM output value is
+the one of trip point N. The automatic control mode is less flexible
+than the manual control mode, but it reacts faster, is more robust and
+doesn't use CPU cycles.
+
+Trip points must be set properly before switching to automatic fan speed
+control mode. The driver will perform basic integrity checks before
+actually switching to automatic control mode.
index 93d8e3d5515018e490629da68423c03ac9565e02..6a03dd4bcc94add14fd99a1275d773e0effa3971 100644 (file)
@@ -84,6 +84,10 @@ Supported chips:
     Addresses scanned: I2C 0x4c
     Datasheet: Publicly available at the Maxim website
                http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500
+  * Winbond/Nuvoton W83L771AWG/ASG
+    Prefix: 'w83l771'
+    Addresses scanned: I2C 0x4c
+    Datasheet: Not publicly available, can be requested from Nuvoton
 
 
 Author: Jean Delvare <khali@linux-fr.org>
@@ -147,6 +151,12 @@ MAX6680 and MAX6681:
   * Selectable address
   * Remote sensor type selection
 
+W83L771AWG/ASG
+  * The AWG and ASG variants only differ in package format.
+  * Filter and alert configuration register at 0xBF
+  * Diode ideality factor configuration (remote sensor) at 0xE3
+  * Moving average (depending on conversion rate)
+
 All temperature values are given in degrees Celsius. Resolution
 is 1.0 degree for the local temperature, 0.125 degree for the remote
 temperature, except for the MAX6657, MAX6658 and MAX6659 which have a
@@ -163,6 +173,18 @@ The lm90 driver will not update its values more frequently than every
 other second; reading them more often will do no harm, but will return
 'old' values.
 
+SMBus Alert Support
+-------------------
+
+This driver has basic support for SMBus alert. When an alert is received,
+the status register is read and the faulty temperature channel is logged.
+
+The Analog Devices chips (ADM1032 and ADT7461) do not implement the SMBus
+alert protocol properly so additional care is needed: the ALERT output is
+disabled when an alert is received, and is re-enabled only when the alarm
+is gone. Otherwise the chip would block alerts from other chips in the bus
+as long as the alarm is active.
+
 PEC Support
 -----------
 
diff --git a/Documentation/init.txt b/Documentation/init.txt
new file mode 100644 (file)
index 0000000..535ad5e
--- /dev/null
@@ -0,0 +1,49 @@
+Explaining the dreaded "No init found." boot hang message
+=========================================================
+
+OK, so you've got this pretty unintuitive message (currently located
+in init/main.c) and are wondering what the H*** went wrong.
+Some high-level reasons for failure (listed roughly in order of execution)
+to load the init binary are:
+A) Unable to mount root FS
+B) init binary doesn't exist on rootfs
+C) broken console device
+D) binary exists but dependencies not available
+E) binary cannot be loaded
+
+Detailed explanations:
+0) Set "debug" kernel parameter (in bootloader config file or CONFIG_CMDLINE)
+   to get more detailed kernel messages.
+A) make sure you have the correct root FS type
+   (and root= kernel parameter points to the correct partition),
+   required drivers such as storage hardware (such as SCSI or USB!)
+   and filesystem (ext3, jffs2 etc.) are builtin (alternatively as modules,
+   to be pre-loaded by an initrd)
+C) Possibly a conflict in console= setup --> initial console unavailable.
+   E.g. some serial consoles are unreliable due to serial IRQ issues (e.g.
+   missing interrupt-based configuration).
+   Try using a different console= device or e.g. netconsole= .
+D) e.g. required library dependencies of the init binary such as
+   /lib/ld-linux.so.2 missing or broken. Use readelf -d <INIT>|grep NEEDED
+   to find out which libraries are required.
+E) make sure the binary's architecture matches your hardware.
+   E.g. i386 vs. x86_64 mismatch, or trying to load x86 on ARM hardware.
+   In case you tried loading a non-binary file here (shell script?),
+   you should make sure that the script specifies an interpreter in its shebang
+   header line (#!/...) that is fully working (including its library
+   dependencies). And before tackling scripts, better first test a simple
+   non-script binary such as /bin/sh and confirm its successful execution.
+   To find out more, add code to init/main.c to display kernel_execve()s
+   return values.
+
+Please extend this explanation whenever you find new failure causes
+(after all loading the init binary is a CRITICAL and hard transition step
+which needs to be made as painless as possible), then submit patch to LKML.
+Further TODOs:
+- Implement the various run_init_process() invocations via a struct array
+  which can then store the kernel_execve() result value and on failure
+  log it all by iterating over _all_ results (very important usability fix).
+- try to make the implementation itself more helpful in general,
+  e.g. by providing additional error messages at affected places.
+
+Andreas Mohr <andi at lisas period de>
index 053037a1fe6d3b4943219c55d82dafca593c8d03..2f9115c0ae627f044f4a22266cd7037c9ba10fd8 100644 (file)
@@ -1,6 +1,7 @@
 Title  : Kernel Probes (Kprobes)
 Authors        : Jim Keniston <jkenisto@us.ibm.com>
-       : Prasanna S Panchamukhi <prasanna@in.ibm.com>
+       : Prasanna S Panchamukhi <prasanna.panchamukhi@gmail.com>
+       : Masami Hiramatsu <mhiramat@redhat.com>
 
 CONTENTS
 
@@ -15,6 +16,7 @@ CONTENTS
 9. Jprobes Example
 10. Kretprobes Example
 Appendix A: The kprobes debugfs interface
+Appendix B: The kprobes sysctl interface
 
 1. Concepts: Kprobes, Jprobes, Return Probes
 
@@ -42,13 +44,13 @@ registration/unregistration of a group of *probes. These functions
 can speed up unregistration process when you have to unregister
 a lot of probes at once.
 
-The next three subsections explain how the different types of
-probes work.  They explain certain things that you'll need to
-know in order to make the best use of Kprobes -- e.g., the
-difference between a pre_handler and a post_handler, and how
-to use the maxactive and nmissed fields of a kretprobe.  But
-if you're in a hurry to start using Kprobes, you can skip ahead
-to section 2.
+The next four subsections explain how the different types of
+probes work and how jump optimization works.  They explain certain
+things that you'll need to know in order to make the best use of
+Kprobes -- e.g., the difference between a pre_handler and
+a post_handler, and how to use the maxactive and nmissed fields of
+a kretprobe.  But if you're in a hurry to start using Kprobes, you
+can skip ahead to section 2.
 
 1.1 How Does a Kprobe Work?
 
@@ -161,13 +163,125 @@ In case probed function is entered but there is no kretprobe_instance
 object available, then in addition to incrementing the nmissed count,
 the user entry_handler invocation is also skipped.
 
+1.4 How Does Jump Optimization Work?
+
+If you configured your kernel with CONFIG_OPTPROBES=y (currently
+this option is supported on x86/x86-64, non-preemptive kernel) and
+the "debug.kprobes_optimization" kernel parameter is set to 1 (see
+sysctl(8)), Kprobes tries to reduce probe-hit overhead by using a jump
+instruction instead of a breakpoint instruction at each probepoint.
+
+1.4.1 Init a Kprobe
+
+When a probe is registered, before attempting this optimization,
+Kprobes inserts an ordinary, breakpoint-based kprobe at the specified
+address. So, even if it's not possible to optimize this particular
+probepoint, there'll be a probe there.
+
+1.4.2 Safety Check
+
+Before optimizing a probe, Kprobes performs the following safety checks:
+
+- Kprobes verifies that the region that will be replaced by the jump
+instruction (the "optimized region") lies entirely within one function.
+(A jump instruction is multiple bytes, and so may overlay multiple
+instructions.)
+
+- Kprobes analyzes the entire function and verifies that there is no
+jump into the optimized region.  Specifically:
+  - the function contains no indirect jump;
+  - the function contains no instruction that causes an exception (since
+  the fixup code triggered by the exception could jump back into the
+  optimized region -- Kprobes checks the exception tables to verify this);
+  and
+  - there is no near jump to the optimized region (other than to the first
+  byte).
+
+- For each instruction in the optimized region, Kprobes verifies that
+the instruction can be executed out of line.
+
+1.4.3 Preparing Detour Buffer
+
+Next, Kprobes prepares a "detour" buffer, which contains the following
+instruction sequence:
+- code to push the CPU's registers (emulating a breakpoint trap)
+- a call to the trampoline code which calls user's probe handlers.
+- code to restore registers
+- the instructions from the optimized region
+- a jump back to the original execution path.
+
+1.4.4 Pre-optimization
+
+After preparing the detour buffer, Kprobes verifies that none of the
+following situations exist:
+- The probe has either a break_handler (i.e., it's a jprobe) or a
+post_handler.
+- Other instructions in the optimized region are probed.
+- The probe is disabled.
+In any of the above cases, Kprobes won't start optimizing the probe.
+Since these are temporary situations, Kprobes tries to start
+optimizing it again if the situation is changed.
+
+If the kprobe can be optimized, Kprobes enqueues the kprobe to an
+optimizing list, and kicks the kprobe-optimizer workqueue to optimize
+it.  If the to-be-optimized probepoint is hit before being optimized,
+Kprobes returns control to the original instruction path by setting
+the CPU's instruction pointer to the copied code in the detour buffer
+-- thus at least avoiding the single-step.
+
+1.4.5 Optimization
+
+The Kprobe-optimizer doesn't insert the jump instruction immediately;
+rather, it calls synchronize_sched() for safety first, because it's
+possible for a CPU to be interrupted in the middle of executing the
+optimized region(*).  As you know, synchronize_sched() can ensure
+that all interruptions that were active when synchronize_sched()
+was called are done, but only if CONFIG_PREEMPT=n.  So, this version
+of kprobe optimization supports only kernels with CONFIG_PREEMPT=n.(**)
+
+After that, the Kprobe-optimizer calls stop_machine() to replace
+the optimized region with a jump instruction to the detour buffer,
+using text_poke_smp().
+
+1.4.6 Unoptimization
+
+When an optimized kprobe is unregistered, disabled, or blocked by
+another kprobe, it will be unoptimized.  If this happens before
+the optimization is complete, the kprobe is just dequeued from the
+optimized list.  If the optimization has been done, the jump is
+replaced with the original code (except for an int3 breakpoint in
+the first byte) by using text_poke_smp().
+
+(*)Please imagine that the 2nd instruction is interrupted and then
+the optimizer replaces the 2nd instruction with the jump *address*
+while the interrupt handler is running. When the interrupt
+returns to original address, there is no valid instruction,
+and it causes an unexpected result.
+
+(**)This optimization-safety checking may be replaced with the
+stop-machine method that ksplice uses for supporting a CONFIG_PREEMPT=y
+kernel.
+
+NOTE for geeks:
+The jump optimization changes the kprobe's pre_handler behavior.
+Without optimization, the pre_handler can change the kernel's execution
+path by changing regs->ip and returning 1.  However, when the probe
+is optimized, that modification is ignored.  Thus, if you want to
+tweak the kernel's execution path, you need to suppress optimization,
+using one of the following techniques:
+- Specify an empty function for the kprobe's post_handler or break_handler.
+ or
+- Config CONFIG_OPTPROBES=n.
+ or
+- Execute 'sysctl -w debug.kprobes_optimization=n'
+
 2. Architectures Supported
 
 Kprobes, jprobes, and return probes are implemented on the following
 architectures:
 
-- i386
-- x86_64 (AMD-64, EM64T)
+- i386 (Supports jump optimization)
+- x86_64 (AMD-64, EM64T) (Supports jump optimization)
 - ppc64
 - ia64 (Does not support probes on instruction slot1.)
 - sparc64 (Return probes not yet implemented.)
@@ -193,6 +307,10 @@ it useful to "Compile the kernel with debug info" (CONFIG_DEBUG_INFO),
 so you can use "objdump -d -l vmlinux" to see the source-to-object
 code mapping.
 
+If you want to reduce probing overhead, set "Kprobes jump optimization
+support" (CONFIG_OPTPROBES) to "y". You can find this option under the
+"Kprobes" line.
+
 4. API Reference
 
 The Kprobes API includes a "register" function and an "unregister"
@@ -389,7 +507,10 @@ the probe which has been registered.
 
 Kprobes allows multiple probes at the same address.  Currently,
 however, there cannot be multiple jprobes on the same function at
-the same time.
+the same time.  Also, a probepoint for which there is a jprobe or
+a post_handler cannot be optimized.  So if you install a jprobe,
+or a kprobe with a post_handler, at an optimized probepoint, the
+probepoint will be unoptimized automatically.
 
 In general, you can install a probe anywhere in the kernel.
 In particular, you can probe interrupt handlers.  Known exceptions
@@ -453,6 +574,38 @@ reason, Kprobes doesn't support return probes (or kprobes or jprobes)
 on the x86_64 version of __switch_to(); the registration functions
 return -EINVAL.
 
+On x86/x86-64, since the Jump Optimization of Kprobes modifies
+instructions widely, there are some limitations to optimization. To
+explain it, we introduce some terminology. Imagine a 3-instruction
+sequence consisting of a two 2-byte instructions and one 3-byte
+instruction.
+
+        IA
+         |
+[-2][-1][0][1][2][3][4][5][6][7]
+        [ins1][ins2][  ins3 ]
+       [<-     DCR       ->]
+          [<- JTPR ->]
+
+ins1: 1st Instruction
+ins2: 2nd Instruction
+ins3: 3rd Instruction
+IA:  Insertion Address
+JTPR: Jump Target Prohibition Region
+DCR: Detoured Code Region
+
+The instructions in DCR are copied to the out-of-line buffer
+of the kprobe, because the bytes in DCR are replaced by
+a 5-byte jump instruction. So there are several limitations.
+
+a) The instructions in DCR must be relocatable.
+b) The instructions in DCR must not include a call instruction.
+c) JTPR must not be targeted by any jump or call instruction.
+d) DCR must not straddle the border betweeen functions.
+
+Anyway, these limitations are checked by the in-kernel instruction
+decoder, so you don't need to worry about that.
+
 6. Probe Overhead
 
 On a typical CPU in use in 2005, a kprobe hit takes 0.5 to 1.0
@@ -476,6 +629,19 @@ k = 0.49 usec; j = 0.76; r = 0.80; kr = 0.82; jr = 1.07
 ppc64: POWER5 (gr), 1656 MHz (SMT disabled, 1 virtual CPU per physical CPU)
 k = 0.77 usec; j = 1.31; r = 1.26; kr = 1.45; jr = 1.99
 
+6.1 Optimized Probe Overhead
+
+Typically, an optimized kprobe hit takes 0.07 to 0.1 microseconds to
+process. Here are sample overhead figures (in usec) for x86 architectures.
+k = unoptimized kprobe, b = boosted (single-step skipped), o = optimized kprobe,
+r = unoptimized kretprobe, rb = boosted kretprobe, ro = optimized kretprobe.
+
+i386: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
+k = 0.80 usec; b = 0.33; o = 0.05; r = 1.10; rb = 0.61; ro = 0.33
+
+x86-64: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
+k = 0.99 usec; b = 0.43; o = 0.06; r = 1.24; rb = 0.68; ro = 0.30
+
 7. TODO
 
 a. SystemTap (http://sourceware.org/systemtap): Provides a simplified
@@ -523,7 +689,8 @@ is also specified. Following columns show probe status. If the probe is on
 a virtual address that is no longer valid (module init sections, module
 virtual addresses that correspond to modules that've been unloaded),
 such probes are marked with [GONE]. If the probe is temporarily disabled,
-such probes are marked with [DISABLED].
+such probes are marked with [DISABLED]. If the probe is optimized, it is
+marked with [OPTIMIZED].
 
 /sys/kernel/debug/kprobes/enabled: Turn kprobes ON/OFF forcibly.
 
@@ -533,3 +700,19 @@ registered probes will be disarmed, till such time a "1" is echoed to this
 file. Note that this knob just disarms and arms all kprobes and doesn't
 change each probe's disabling state. This means that disabled kprobes (marked
 [DISABLED]) will be not enabled if you turn ON all kprobes by this knob.
+
+
+Appendix B: The kprobes sysctl interface
+
+/proc/sys/debug/kprobes-optimization: Turn kprobes optimization ON/OFF.
+
+When CONFIG_OPTPROBES=y, this sysctl interface appears and it provides
+a knob to globally and forcibly turn jump optimization (see section
+1.4) ON or OFF. By default, jump optimization is allowed (ON).
+If you echo "0" to this file or set "debug.kprobes_optimization" to
+0 via sysctl, all optimized probes will be unoptimized, and any new
+probes registered after that will not be optimized.  Note that this
+knob *changes* the optimized state. This means that optimized probes
+(marked [OPTIMIZED]) will be unoptimized ([OPTIMIZED] tag will be
+removed). If the knob is turned on, they will be optimized again.
+
index 2811e452f7566f5f5fe4fa6242f8724a9970ba11..c6416a398163155bc64341754b99039aba529502 100644 (file)
@@ -23,12 +23,12 @@ of a virtual machine.  The ioctls belong to three classes
    Only run vcpu ioctls from the same thread that was used to create the
    vcpu.
 
-2. File descritpors
+2. File descriptors
 
 The kvm API is centered around file descriptors.  An initial
 open("/dev/kvm") obtains a handle to the kvm subsystem; this handle
 can be used to issue system ioctls.  A KVM_CREATE_VM ioctl on this
-handle will create a VM file descripror which can be used to issue VM
+handle will create a VM file descriptor which can be used to issue VM
 ioctls.  A KVM_CREATE_VCPU ioctl on a VM fd will create a virtual cpu
 and return a file descriptor pointing to it.  Finally, ioctls on a vcpu
 fd can be used to control the vcpu, including the important task of
@@ -643,7 +643,7 @@ Type: vm ioctl
 Parameters: struct kvm_clock_data (in)
 Returns: 0 on success, -1 on error
 
-Sets the current timestamp of kvmclock to the valued specific in its parameter.
+Sets the current timestamp of kvmclock to the value specified in its parameter.
 In conjunction with KVM_GET_CLOCK, it is used to ensure monotonicity on scenarios
 such as migration.
 
@@ -795,11 +795,11 @@ Unused.
                        __u64 data_offset; /* relative to kvm_run start */
                } io;
 
-If exit_reason is KVM_EXIT_IO_IN or KVM_EXIT_IO_OUT, then the vcpu has
+If exit_reason is KVM_EXIT_IO, then the vcpu has
 executed a port I/O instruction which could not be satisfied by kvm.
 data_offset describes where the data is located (KVM_EXIT_IO_OUT) or
 where kvm expects application code to place the data for the next
-KVM_RUN invocation (KVM_EXIT_IO_IN).  Data format is a patcked array.
+KVM_RUN invocation (KVM_EXIT_IO_IN).  Data format is a packed array.
 
                struct {
                        struct kvm_debug_exit_arch arch;
@@ -815,7 +815,7 @@ Unused.
                        __u8  is_write;
                } mmio;
 
-If exit_reason is KVM_EXIT_MMIO or KVM_EXIT_IO_OUT, then the vcpu has
+If exit_reason is KVM_EXIT_MMIO, then the vcpu has
 executed a memory-mapped I/O instruction which could not be satisfied
 by kvm.  The 'data' member contains the written data if 'is_write' is
 true, and should be filled by application code otherwise.
index 356fd86f4ea8279b1a6175410e076f460f9a7da6..ab00eeddecafc0e5cadfa0e6afa2bc758ccf53b9 100644 (file)
@@ -224,6 +224,12 @@ defined in include/linux/pm.h:
       RPM_SUSPENDED, which means that each device is initially regarded by the
       PM core as 'suspended', regardless of its real hardware status
 
+  unsigned int runtime_auto;
+    - if set, indicates that the user space has allowed the device driver to
+      power manage the device at run time via the /sys/devices/.../power/control
+      interface; it may only be modified with the help of the pm_runtime_allow()
+      and pm_runtime_forbid() helper functions
+
 All of the above fields are members of the 'power' member of 'struct device'.
 
 4. Run-time PM Device Helper Functions
@@ -329,6 +335,20 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
       'power.runtime_error' is set or 'power.disable_depth' is greater than
       zero)
 
+  bool pm_runtime_suspended(struct device *dev);
+    - return true if the device's runtime PM status is 'suspended', or false
+      otherwise
+
+  void pm_runtime_allow(struct device *dev);
+    - set the power.runtime_auto flag for the device and decrease its usage
+      counter (used by the /sys/devices/.../power/control interface to
+      effectively allow the device to be power managed at run time)
+
+  void pm_runtime_forbid(struct device *dev);
+    - unset the power.runtime_auto flag for the device and increase its usage
+      counter (used by the /sys/devices/.../power/control interface to
+      effectively prevent the device from being power managed at run time)
+
 It is safe to execute the following helper functions from interrupt context:
 
 pm_request_idle()
@@ -382,6 +402,18 @@ may be desirable to suspend the device as soon as ->probe() or ->remove() has
 finished, so the PM core uses pm_runtime_idle_sync() to invoke the
 subsystem-level idle callback for the device at that time.
 
+The user space can effectively disallow the driver of the device to power manage
+it at run time by changing the value of its /sys/devices/.../power/control
+attribute to "on", which causes pm_runtime_forbid() to be called.  In principle,
+this mechanism may also be used by the driver to effectively turn off the
+run-time power management of the device until the user space turns it on.
+Namely, during the initialization the driver can make sure that the run-time PM
+status of the device is 'active' and call pm_runtime_forbid().  It should be
+noted, however, that if the user space has already intentionally changed the
+value of /sys/devices/.../power/control to "auto" to allow the driver to power
+manage the device at run time, the driver may confuse it by using
+pm_runtime_forbid() this way.
+
 6. Run-time PM and System Sleep
 
 Run-time PM and system sleep (i.e., system suspend and hibernation, also known
@@ -431,3 +463,64 @@ The PM core always increments the run-time usage counter before calling the
 ->prepare() callback and decrements it after calling the ->complete() callback.
 Hence disabling run-time PM temporarily like this will not cause any run-time
 suspend callbacks to be lost.
+
+7. Generic subsystem callbacks
+
+Subsystems may wish to conserve code space by using the set of generic power
+management callbacks provided by the PM core, defined in
+driver/base/power/generic_ops.c:
+
+  int pm_generic_runtime_idle(struct device *dev);
+    - invoke the ->runtime_idle() callback provided by the driver of this
+      device, if defined, and call pm_runtime_suspend() for this device if the
+      return value is 0 or the callback is not defined
+
+  int pm_generic_runtime_suspend(struct device *dev);
+    - invoke the ->runtime_suspend() callback provided by the driver of this
+      device and return its result, or return -EINVAL if not defined
+
+  int pm_generic_runtime_resume(struct device *dev);
+    - invoke the ->runtime_resume() callback provided by the driver of this
+      device and return its result, or return -EINVAL if not defined
+
+  int pm_generic_suspend(struct device *dev);
+    - if the device has not been suspended at run time, invoke the ->suspend()
+      callback provided by its driver and return its result, or return 0 if not
+      defined
+
+  int pm_generic_resume(struct device *dev);
+    - invoke the ->resume() callback provided by the driver of this device and,
+      if successful, change the device's runtime PM status to 'active'
+
+  int pm_generic_freeze(struct device *dev);
+    - if the device has not been suspended at run time, invoke the ->freeze()
+      callback provided by its driver and return its result, or return 0 if not
+      defined
+
+  int pm_generic_thaw(struct device *dev);
+    - if the device has not been suspended at run time, invoke the ->thaw()
+      callback provided by its driver and return its result, or return 0 if not
+      defined
+
+  int pm_generic_poweroff(struct device *dev);
+    - if the device has not been suspended at run time, invoke the ->poweroff()
+      callback provided by its driver and return its result, or return 0 if not
+      defined
+
+  int pm_generic_restore(struct device *dev);
+    - invoke the ->restore() callback provided by the driver of this device and,
+      if successful, change the device's runtime PM status to 'active'
+
+These functions can be assigned to the ->runtime_idle(), ->runtime_suspend(),
+->runtime_resume(), ->suspend(), ->resume(), ->freeze(), ->thaw(), ->poweroff(),
+or ->restore() callback pointers in the subsystem-level dev_pm_ops structures.
+
+If a subsystem wishes to use all of them at the same time, it can simply assign
+the GENERIC_SUBSYS_PM_OPS macro, defined in include/linux/pm.h, to its
+dev_pm_ops structure pointer.
+
+Device drivers that wish to use the same function as a system suspend, freeze,
+poweroff and run-time suspend callback, and similarly for system resume, thaw,
+restore, and run-time resume, can achieve this with the help of the
+UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its
+last argument to NULL).
index 0732cdd05ba1e43b9ff05f336036938de588ad0b..2a4b4bce6110af59579c6f29e36fb8bcd71f7428 100644 (file)
@@ -44,21 +44,29 @@ Example:
                        compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel";
                        cell-index = <0>;
                        reg = <0 0x80>;
+                       interrupt-parent = <&ipic>;
+                       interrupts = <71 8>;
                };
                dma-channel@80 {
                        compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel";
                        cell-index = <1>;
                        reg = <0x80 0x80>;
+                       interrupt-parent = <&ipic>;
+                       interrupts = <71 8>;
                };
                dma-channel@100 {
                        compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel";
                        cell-index = <2>;
                        reg = <0x100 0x80>;
+                       interrupt-parent = <&ipic>;
+                       interrupts = <71 8>;
                };
                dma-channel@180 {
                        compatible = "fsl,mpc8349-dma-channel", "fsl,elo-dma-channel";
                        cell-index = <3>;
                        reg = <0x180 0x80>;
+                       interrupt-parent = <&ipic>;
+                       interrupts = <71 8>;
                };
        };
 
index b37300edf27c8bc528700f00b9045bd3f6a20b2d..07375e73981a8747f1432213691a4187ccf6544a 100644 (file)
@@ -41,6 +41,7 @@ Possible debug options are
        P               Poisoning (object and padding)
        U               User tracking (free and alloc)
        T               Trace (please only use on single slabs)
+       A               Toggle failslab filter mark for the cache
        O               Switch debugging off for caches that would have
                        caused higher minimum slab orders
        -               Switch all debugging off (useful if the kernel is
index c6591bca646b3599703a3ca0ab0727ae2e53165b..c8a8b1fd58b36d751fe31b308c8c76ba721cab0f 100644 (file)
@@ -71,6 +71,7 @@ Descriptions of section entries:
        M: Mail patches to: FullName <address@domain>
        L: Mailing list that is relevant to this area
        W: Web-page with status/info
+       Q: Patchwork web based patch tracking system site
        T: SCM tree type and location.  Type is one of: git, hg, quilt, stgit.
        S: Status, one of the following:
           Supported:   Someone is actually paid to look after this.
@@ -182,6 +183,7 @@ M:  Ron Minnich <rminnich@sandia.gov>
 M:     Latchesar Ionkov <lucho@ionkov.net>
 L:     v9fs-developer@lists.sourceforge.net
 W:     http://swik.net/v9fs
+Q:     http://patchwork.kernel.org/project/v9fs-devel/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git
 S:     Maintained
 F:     Documentation/filesystems/9p.txt
@@ -238,6 +240,7 @@ ACPI
 M:     Len Brown <lenb@kernel.org>
 L:     linux-acpi@vger.kernel.org
 W:     http://www.lesswatts.org/projects/acpi/
+Q:     http://patchwork.kernel.org/project/linux-acpi/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git
 S:     Supported
 F:     drivers/acpi/
@@ -428,7 +431,6 @@ P:  Jordan Crouse
 L:     linux-geode@lists.infradead.org (moderated for non-subscribers)
 W:     http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html
 S:     Supported
-F:     arch/x86/kernel/geode_32.c
 F:     drivers/char/hw_random/geode-rng.c
 F:     drivers/crypto/geode*
 F:     drivers/video/geode/
@@ -966,6 +968,13 @@ W: http://www.arm.linux.org.uk/
 S:     Maintained
 F:     arch/arm/vfp/
 
+ASC7621 HARDWARE MONITOR DRIVER
+M:     George Joseph <george.joseph@fairview5.com>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     Documentation/hwmon/asc7621
+F:     drivers/hwmon/asc7621.c
+
 ASUS ACPI EXTRAS DRIVER
 M:     Corentin Chary <corentincj@iksaif.net>
 M:     Karol Kozimor <sziwan@users.sourceforge.net>
@@ -1332,6 +1341,7 @@ BTRFS FILE SYSTEM
 M:     Chris Mason <chris.mason@oracle.com>
 L:     linux-btrfs@vger.kernel.org
 W:     http://btrfs.wiki.kernel.org/
+Q:     http://patchwork.kernel.org/project/linux-btrfs/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable.git
 S:     Maintained
 F:     Documentation/filesystems/btrfs.txt
@@ -1496,6 +1506,7 @@ M:        Steve French <sfrench@samba.org>
 L:     linux-cifs-client@lists.samba.org (moderated for non-subscribers)
 L:     samba-technical@lists.samba.org (moderated for non-subscribers)
 W:     http://linux-cifs.samba.org/
+Q:     http://patchwork.ozlabs.org/project/linux-cifs-client/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
 S:     Supported
 F:     Documentation/filesystems/cifs.txt
@@ -1782,6 +1793,7 @@ DEVICE-MAPPER  (LVM)
 P:     Alasdair Kergon
 L:     dm-devel@redhat.com
 W:     http://sources.redhat.com/dm
+Q:     http://patchwork.kernel.org/project/dm-devel/list/
 S:     Maintained
 F:     Documentation/device-mapper/
 F:     drivers/md/dm*
@@ -2126,6 +2138,7 @@ M:        "Theodore Ts'o" <tytso@mit.edu>
 M:     Andreas Dilger <adilger@sun.com>
 L:     linux-ext4@vger.kernel.org
 W:     http://ext4.wiki.kernel.org
+Q:     http://patchwork.ozlabs.org/project/linux-ext4/list/
 S:     Maintained
 F:     Documentation/filesystems/ext4.txt
 F:     fs/ext4/
@@ -2502,13 +2515,6 @@ L:       linux-parisc@vger.kernel.org
 S:     Maintained
 F:     sound/parisc/harmony.*
 
-HAYES ESP SERIAL DRIVER
-M:     "Andrew J. Robinson" <arobinso@nyx.net>
-W:     http://www.nyx.net/~arobinso
-S:     Maintained
-F:     Documentation/serial/hayes-esp.txt
-F:     drivers/char/esp.c
-
 HEWLETT-PACKARD SMART2 RAID DRIVER
 M:     Chirag Kantharia <chirag.kantharia@hp.com>
 L:     iss_storagedev@hp.com
@@ -2717,6 +2723,7 @@ F:        drivers/scsi/ips.*
 IDE SUBSYSTEM
 M:     "David S. Miller" <davem@davemloft.net>
 L:     linux-ide@vger.kernel.org
+Q:     http://patchwork.ozlabs.org/project/linux-ide/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide-2.6.git
 S:     Maintained
 F:     Documentation/ide/
@@ -2771,6 +2778,7 @@ M:        Sean Hefty <sean.hefty@intel.com>
 M:     Hal Rosenstock <hal.rosenstock@gmail.com>
 L:     linux-rdma@vger.kernel.org
 W:     http://www.openib.org/
+Q:     http://patchwork.kernel.org/project/linux-rdma/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/roland/infiniband.git
 S:     Supported
 F:     Documentation/infiniband/
@@ -2790,6 +2798,7 @@ INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS
 M:     Dmitry Torokhov <dmitry.torokhov@gmail.com>
 M:     Dmitry Torokhov <dtor@mail.ru>
 L:     linux-input@vger.kernel.org
+Q:     http://patchwork.kernel.org/project/linux-input/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git
 S:     Maintained
 F:     drivers/input/
@@ -3046,6 +3055,13 @@ W:       http://www.melware.de
 S:     Maintained
 F:     drivers/isdn/hardware/eicon/
 
+IT87 HARDWARE MONITORING DRIVER
+M:     Jean Delvare <khali@linux-fr.org>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     Documentation/hwmon/it87
+F:     drivers/hwmon/it87.c
+
 IVTV VIDEO4LINUX DRIVER
 M:     Andy Walls <awalls@radix.net>
 L:     ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
@@ -3099,6 +3115,7 @@ F:        drivers/hwmon/k8temp.c
 KCONFIG
 M:     Roman Zippel <zippel@linux-m68k.org>
 L:     linux-kbuild@vger.kernel.org
+Q:     http://patchwork.kernel.org/project/linux-kbuild/list/
 S:     Maintained
 F:     Documentation/kbuild/kconfig-language.txt
 F:     scripts/kconfig/
@@ -3173,7 +3190,7 @@ F:        arch/x86/include/asm/svm.h
 F:     arch/x86/kvm/svm.c
 
 KERNEL VIRTUAL MACHINE (KVM) FOR POWERPC
-M:     Hollis Blanchard <hollisb@us.ibm.com>
+M:     Alexander Graf <agraf@suse.de>
 L:     kvm-ppc@vger.kernel.org
 W:     http://kvm.qumranet.com
 S:     Supported
@@ -3312,6 +3329,7 @@ M:        Benjamin Herrenschmidt <benh@kernel.crashing.org>
 M:     Paul Mackerras <paulus@samba.org>
 W:     http://www.penguinppc.org/
 L:     linuxppc-dev@ozlabs.org
+Q:     http://patchwork.ozlabs.org/project/linuxppc-dev/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git
 S:     Supported
 F:     Documentation/powerpc/
@@ -3432,6 +3450,13 @@ S:       Maintained
 F:     Documentation/ldm.txt
 F:     fs/partitions/ldm.*
 
+LogFS
+M:     Joern Engel <joern@logfs.org>
+L:     logfs@logfs.org
+W:     logfs.org
+S:     Maintained
+F:     fs/logfs/
+
 LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
 M:     Eric Moore <Eric.Moore@lsi.com>
 M:     support@lsi.com
@@ -3568,6 +3593,7 @@ M:        Mauro Carvalho Chehab <mchehab@infradead.org>
 P:     LinuxTV.org Project
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
+Q:     http://patchwork.kernel.org/project/linux-media/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6.git
 S:     Maintained
 F:     Documentation/dvb/
@@ -3603,8 +3629,9 @@ F:        mm/memcontrol.c
 
 MEMORY TECHNOLOGY DEVICES (MTD)
 M:     David Woodhouse <dwmw2@infradead.org>
-W:     http://www.linux-mtd.infradead.org/
 L:     linux-mtd@lists.infradead.org
+W:     http://www.linux-mtd.infradead.org/
+Q:     http://patchwork.ozlabs.org/project/linux-mtd/list/
 T:     git git://git.infradead.org/mtd-2.6.git
 S:     Maintained
 F:     drivers/mtd/
@@ -3864,6 +3891,7 @@ S:        Maintained
 NETWORKING [WIRELESS]
 M:     "John W. Linville" <linville@tuxdriver.com>
 L:     linux-wireless@vger.kernel.org
+Q:     http://patchwork.kernel.org/project/linux-wireless/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git
 S:     Maintained
 F:     net/mac80211/
@@ -3956,6 +3984,7 @@ M:        Tony Lindgren <tony@atomide.com>
 L:     linux-omap@vger.kernel.org
 W:     http://www.muru.com/linux/omap/
 W:     http://linux.omap.com/
+Q:     http://patchwork.kernel.org/project/linux-omap/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6.git
 S:     Maintained
 F:     arch/arm/*omap*/
@@ -4182,6 +4211,7 @@ M:        Helge Deller <deller@gmx.de>
 M:     "James E.J. Bottomley" <jejb@parisc-linux.org>
 L:     linux-parisc@vger.kernel.org
 W:     http://www.parisc-linux.org/
+Q:     http://patchwork.kernel.org/project/linux-parisc/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kyle/parisc-2.6.git
 S:     Maintained
 F:     arch/parisc/
@@ -4224,6 +4254,7 @@ F:        Documentation/powerpc/eeh-pci-error-recovery.txt
 PCI SUBSYSTEM
 M:     Jesse Barnes <jbarnes@virtuousgeek.org>
 L:     linux-pci@vger.kernel.org
+Q:     http://patchwork.kernel.org/project/linux-pci/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6.git
 S:     Supported
 F:     Documentation/PCI/
@@ -4265,7 +4296,9 @@ M:        Ingo Molnar <mingo@elte.hu>
 S:     Supported
 F:     kernel/perf_event.c
 F:     include/linux/perf_event.h
-F:     arch/*/*/kernel/perf_event.c
+F:     arch/*/kernel/perf_event.c
+F:     arch/*/kernel/*/perf_event.c
+F:     arch/*/kernel/*/*/perf_event.c
 F:     arch/*/include/asm/perf_event.h
 F:     arch/*/lib/perf_event.c
 F:     arch/*/kernel/perf_callchain.c
@@ -4599,6 +4632,7 @@ F:        include/linux/rtc.h
 REAL TIME CLOCK (RTC) SUBSYSTEM
 M:     Alessandro Zummo <a.zummo@towertech.it>
 L:     rtc-linux@googlegroups.com
+Q:     http://patchwork.ozlabs.org/project/rtc-linux/list/
 S:     Maintained
 F:     Documentation/rtc.txt
 F:     drivers/rtc/
@@ -4966,6 +5000,7 @@ F:        drivers/*/*/*s3c2410*
 TI DAVINCI MACHINE SUPPORT
 P:     Kevin Hilman
 M:     davinci-linux-open-source@linux.davincidsp.com
+Q:     http://patchwork.kernel.org/project/linux-davinci/list/
 S:     Supported
 F:     arch/arm/mach-davinci
 
@@ -5131,6 +5166,7 @@ F:        include/sound/soc*
 SPARC + UltraSPARC (sparc/sparc64)
 M:     "David S. Miller" <davem@davemloft.net>
 L:     sparclinux@vger.kernel.org
+Q:     http://patchwork.ozlabs.org/project/sparclinux/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next-2.6.git
 S:     Maintained
@@ -5146,6 +5182,7 @@ SPI SUBSYSTEM
 M:     David Brownell <dbrownell@users.sourceforge.net>
 M:     Grant Likely <grant.likely@secretlab.ca>
 L:     spi-devel-general@lists.sourceforge.net
+Q:     http://patchwork.kernel.org/project/spi-devel-general/list/
 S:     Maintained
 F:     Documentation/spi/
 F:     drivers/spi/
@@ -5201,7 +5238,7 @@ F:        drivers/net/starfire*
 
 STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
 S:     Orphan
-F:     drivers/net/wireless/strip.c
+F:     drivers/staging/strip/strip.c
 F:     include/linux/if_strip.h
 
 STRADIS MPEG-2 DECODER DRIVER
@@ -5222,6 +5259,7 @@ SUPERH
 M:     Paul Mundt <lethal@linux-sh.org>
 L:     linux-sh@vger.kernel.org
 W:     http://www.linux-sh.org
+Q:     http://patchwork.kernel.org/project/linux-sh/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6.git
 S:     Supported
 F:     Documentation/sh/
@@ -5989,7 +6027,7 @@ L:        linux-wireless@vger.kernel.org
 W:     http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/
 S:     Maintained
 F:     Documentation/networking/wavelan.txt
-F:     drivers/net/wireless/wavelan*
+F:     drivers/staging/wavelan/
 
 WD7000 SCSI DRIVER
 M:     Miroslav Zagorac <zaga@fly.cc.fer.hr>
@@ -6185,6 +6223,7 @@ F:        drivers/serial/zs.*
 THE REST
 M:     Linus Torvalds <torvalds@linux-foundation.org>
 L:     linux-kernel@vger.kernel.org
+Q:     http://patchwork.kernel.org/project/LKML/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
 S:     Buried alive in reporters
 F:     *
index 215e46073c453d63a691b4fc02bde826a9d4ad50..e5eb1337a5377f5b131b2ba70efd8263b8057404 100644 (file)
@@ -41,6 +41,17 @@ config KPROBES
          for kernel debugging, non-intrusive instrumentation and testing.
          If in doubt, say "N".
 
+config OPTPROBES
+       bool "Kprobes jump optimization support (EXPERIMENTAL)"
+       default y
+       depends on KPROBES
+       depends on !PREEMPT
+       depends on HAVE_OPTPROBES
+       select KALLSYMS_ALL
+       help
+         This option will allow kprobes to optimize breakpoint to
+         a jump for reducing its overhead.
+
 config HAVE_EFFICIENT_UNALIGNED_ACCESS
        bool
        help
@@ -83,6 +94,8 @@ config HAVE_KPROBES
 config HAVE_KRETPROBES
        bool
 
+config HAVE_OPTPROBES
+       bool
 #
 # An arch should select this if it provides all these things:
 #
index 62619f25132f93018d3a7850dda6c91aee5d0eb2..53c213f70fcbc8151b42fea8b50998bf509e5fea 100644 (file)
@@ -361,7 +361,7 @@ osf_procfs_mount(char *dirname, struct procfs_args __user *args, int flags)
 SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, char __user *, path,
                int, flag, void __user *, data)
 {
-       int retval = -EINVAL;
+       int retval;
        char *name;
 
        name = getname(path);
@@ -379,6 +379,7 @@ SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, char __user *, path,
                retval = osf_procfs_mount(name, data, flag);
                break;
        default:
+               retval = -EINVAL;
                printk("osf_mount(%ld, %x)\n", typenr, flag);
        }
        putname(name);
index 1a8c7279a28b39eb8473d5e5ffb383cfbc5040ec..9b28f1243bdc1d96c2be3dac0ad947a72a61a264 100644 (file)
@@ -366,8 +366,7 @@ static inline int iop_chan_xor_slot_count(size_t len, int src_cnt,
                slot_cnt += *slots_per_op;
        }
 
-       if (len)
-               slot_cnt += *slots_per_op;
+       slot_cnt += *slots_per_op;
 
        return slot_cnt;
 }
@@ -389,8 +388,7 @@ static inline int iop_chan_zero_sum_slot_count(size_t len, int src_cnt,
                slot_cnt += *slots_per_op;
        }
 
-       if (len)
-               slot_cnt += *slots_per_op;
+       slot_cnt += *slots_per_op;
 
        return slot_cnt;
 }
@@ -737,10 +735,8 @@ iop_desc_set_zero_sum_byte_count(struct iop_adma_desc_slot *desc, u32 len)
                        i += slots_per_op;
                } while (len > IOP_ADMA_ZERO_SUM_MAX_BYTE_COUNT);
 
-               if (len) {
-                       iter = iop_hw_desc_slot_idx(hw_desc, i);
-                       iter->byte_count = len;
-               }
+               iter = iop_hw_desc_slot_idx(hw_desc, i);
+               iter->byte_count = len;
        }
 }
 
index f4cfee9c7d286db0dd3f3d4f9a02e71446d748b3..b8155b4e5ffa149cb065529e74e9ad636ad31f1b 100644 (file)
@@ -53,7 +53,7 @@ struct coh901318_params {
  * struct coh_dma_channel - dma channel base
  * @name: ascii name of dma channel
  * @number: channel id number
- * @desc_nbr_max: number of preallocated descriptortors
+ * @desc_nbr_max: number of preallocated descriptors
  * @priority_high: prio of channel, 0 low otherwise high.
  * @param: configuration parameters
  * @dev_addr: physical address of periphal connected to channel
index 7adac388a77142b2cb7e5d5db4b5483f473c9476..059eac6abda1d6f7f842da45647f4307125c767d 100644 (file)
@@ -20,6 +20,12 @@ config RWSEM_GENERIC_SPINLOCK
 config RWSEM_XCHGADD_ALGORITHM
        bool
 
+config GENERIC_TIME
+       def_bool y
+
+config ARCH_USES_GETTIMEOFFSET
+       def_bool y
+
 config GENERIC_IOMAP
        bool
        default y
index fd529a0ec758aa3a018df065729616310a39cf12..b70fb34939d97b68859f2fadbf042253ec70d659 100644 (file)
@@ -628,9 +628,9 @@ static int create_output_descriptors(struct cryptocop_operation *operation, int
                cdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.indata[*iniov_ix].iov_base + *iniov_offset);
                cdesc->dma_descr->after = cdesc->dma_descr->buf + dlength;
 
+               assert(desc_len >= dlength);
                desc_len -= dlength;
                *iniov_offset += dlength;
-               assert(desc_len >= 0);
                if (*iniov_offset >= operation->tfrm_op.indata[*iniov_ix].iov_len) {
                        *iniov_offset = 0;
                        ++(*iniov_ix);
index 84d31bd7b692a04167804d436cd67e9e1eb047aa..82ef293c4c816bfacfe6bf3fb8df8d8892427be7 100644 (file)
@@ -332,7 +332,7 @@ int crisv32_arbiter_unwatch(int id)
        if (id == 0)
                intr_mask.bp0 = regk_marb_no;
        else if (id == 1)
-               intr_mask.bp2 = regk_marb_no;
+               intr_mask.bp1 = regk_marb_no;
        else if (id == 2)
                intr_mask.bp2 = regk_marb_no;
        else if (id == 3)
index 074fe7dea96bbaa44c27f6ef513253552646a756..a05dd31f3efb5121d07cb8a826e8d6e063f88824 100644 (file)
@@ -42,75 +42,11 @@ unsigned long loops_per_usec;
 extern unsigned long do_slow_gettimeoffset(void);
 static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
 
-/*
- * This version of gettimeofday has near microsecond resolution.
- *
- * Note: Division is quite slow on CRIS and do_gettimeofday is called
- *       rather often. Maybe we should do some kind of approximation here
- *       (a naive approximation would be to divide by 1024).
- */
-void do_gettimeofday(struct timeval *tv)
-{
-       unsigned long flags;
-       signed long usec, sec;
-       local_irq_save(flags);
-       usec = do_gettimeoffset();
-
-        /*
-        * If time_adjust is negative then NTP is slowing the clock
-        * so make sure not to go into next possible interval.
-        * Better to lose some accuracy than have time go backwards..
-        */
-       if (unlikely(time_adjust < 0) && usec > tickadj)
-               usec = tickadj;
-
-       sec = xtime.tv_sec;
-       usec += xtime.tv_nsec / 1000;
-       local_irq_restore(flags);
-
-       while (usec >= 1000000) {
-               usec -= 1000000;
-               sec++;
-       }
-
-       tv->tv_sec = sec;
-       tv->tv_usec = usec;
-}
-
-EXPORT_SYMBOL(do_gettimeofday);
-
-int do_settimeofday(struct timespec *tv)
+u32 arch_gettimeoffset(void)
 {
-       time_t wtm_sec, sec = tv->tv_sec;
-       long wtm_nsec, nsec = tv->tv_nsec;
-
-       if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
-               return -EINVAL;
-
-       write_seqlock_irq(&xtime_lock);
-       /*
-        * This is revolting. We need to set "xtime" correctly. However, the
-        * value in this location is the value at the most recent update of
-        * wall time.  Discover what correction gettimeofday() would have
-        * made, and then undo it!
-        */
-       nsec -= do_gettimeoffset() * NSEC_PER_USEC;
-
-       wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
-       wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
-
-       set_normalized_timespec(&xtime, sec, nsec);
-       set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
-
-       ntp_clear();
-       write_sequnlock_irq(&xtime_lock);
-       clock_was_set();
-       return 0;
+       return do_gettimeoffset() * 1000;
 }
 
-EXPORT_SYMBOL(do_settimeofday);
-
-
 /*
  * BUG: This routine does not handle hour overflow properly; it just
  *      sets the minutes. Usually you'll only notice that after reboot!
index 492b5c4dfed606d3004faf808b7a7270c3c94d0d..8c7260a3cd41ac960f7f0e46a8353925a2518061 100644 (file)
@@ -68,41 +68,4 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev,
 #define PCIBIOS_MIN_IO         0x100
 #define PCIBIOS_MIN_MEM                0x00010000
 
-/* Make physical memory consistent for a single
- * streaming mode DMA translation after a transfer.
- *
- * If you perform a pci_map_single() but wish to interrogate the
- * buffer using the cpu, yet do not wish to teardown the PCI dma
- * mapping, you must call this function before doing so.  At the
- * next point you give the PCI dma address back to the card, the
- * device again owns the buffer.
- */
-static inline void pci_dma_sync_single(struct pci_dev *hwdev,
-                                      dma_addr_t dma_handle,
-                                      size_t size, int direction)
-{
-       BUG_ON(direction == PCI_DMA_NONE);
-
-       frv_cache_wback_inv((unsigned long)bus_to_virt(dma_handle),
-                           (unsigned long)bus_to_virt(dma_handle) + size);
-}
-
-/* Make physical memory consistent for a set of streaming
- * mode DMA translations after a transfer.
- *
- * The same as pci_dma_sync_single but for a scatter-gather list,
- * same rules and usage.
- */
-static inline void pci_dma_sync_sg(struct pci_dev *hwdev,
-                                  struct scatterlist *sg,
-                                  int nelems, int direction)
-{
-       int i;
-       BUG_ON(direction == PCI_DMA_NONE);
-
-       for (i = 0; i < nelems; i++)
-               frv_cache_wback_inv(sg_dma_address(&sg[i]),
-                                   sg_dma_address(&sg[i])+sg_dma_len(&sg[i]));
-}
-
 #endif /* _ASM_FRV_PCI_H */
index 4c41656ede87e828e95226fabf409fedc09406ca..b5298eb09adb20b17a4cad2ea144d28d407d2812 100644 (file)
@@ -219,54 +219,6 @@ do {                                                                               \
        NEW_AUX_ENT(AT_SYSINFO_EHDR, (unsigned long) GATE_EHDR);                \
 } while (0)
 
-
-/*
- * These macros parameterize elf_core_dump in fs/binfmt_elf.c to write out
- * extra segments containing the gate DSO contents.  Dumping its
- * contents makes post-mortem fully interpretable later without matching up
- * the same kernel and hardware config to see what PC values meant.
- * Dumping its extra ELF program headers includes all the other information
- * a debugger needs to easily find how the gate DSO was being used.
- */
-#define ELF_CORE_EXTRA_PHDRS           (GATE_EHDR->e_phnum)
-#define ELF_CORE_WRITE_EXTRA_PHDRS                                             \
-do {                                                                           \
-       const struct elf_phdr *const gate_phdrs =                             \
-               (const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);   \
-       int i;                                                                  \
-       Elf64_Off ofs = 0;                                                    \
-       for (i = 0; i < GATE_EHDR->e_phnum; ++i) {                              \
-               struct elf_phdr phdr = gate_phdrs[i];                         \
-               if (phdr.p_type == PT_LOAD) {                                   \
-                       phdr.p_memsz = PAGE_ALIGN(phdr.p_memsz);              \
-                       phdr.p_filesz = phdr.p_memsz;                         \
-                       if (ofs == 0) {                                       \
-                               ofs = phdr.p_offset = offset;                 \
-                       offset += phdr.p_filesz;                                \
-               }                                                             \
-               else                                                          \
-                               phdr.p_offset = ofs;                          \
-               }                                                             \
-               else                                                          \
-                       phdr.p_offset += ofs;                                   \
-               phdr.p_paddr = 0; /* match other core phdrs */                  \
-               DUMP_WRITE(&phdr, sizeof(phdr));                                \
-       }                                                                       \
-} while (0)
-#define ELF_CORE_WRITE_EXTRA_DATA                                      \
-do {                                                                   \
-       const struct elf_phdr *const gate_phdrs =                             \
-               (const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);   \
-       int i;                                                          \
-       for (i = 0; i < GATE_EHDR->e_phnum; ++i) {                      \
-               if (gate_phdrs[i].p_type == PT_LOAD) {                        \
-                       DUMP_WRITE((void *) gate_phdrs[i].p_vaddr,            \
-                                  PAGE_ALIGN(gate_phdrs[i].p_memsz));        \
-                       break;                                                \
-               }                                                             \
-       }                                                               \
-} while (0)
-
 /*
  * format for entries in the Global Offset Table
  */
index 4138282aefa86acb81a548464d10c0a3527e2508..db10b1e378b0470ec52828a19e78da43e26d31db 100644 (file)
@@ -45,6 +45,8 @@ endif
 obj-$(CONFIG_DMAR)             += pci-dma.o
 obj-$(CONFIG_SWIOTLB)          += pci-swiotlb.o
 
+obj-$(CONFIG_BINFMT_ELF)       += elfcore.o
+
 # fp_emulate() expects f2-f5,f16-f31 to contain the user-level state.
 CFLAGS_traps.o  += -mfixed-range=f2-f5,f16-f31
 
diff --git a/arch/ia64/kernel/elfcore.c b/arch/ia64/kernel/elfcore.c
new file mode 100644 (file)
index 0000000..bac1639
--- /dev/null
@@ -0,0 +1,80 @@
+#include <linux/elf.h>
+#include <linux/coredump.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+#include <asm/elf.h>
+
+
+Elf64_Half elf_core_extra_phdrs(void)
+{
+       return GATE_EHDR->e_phnum;
+}
+
+int elf_core_write_extra_phdrs(struct file *file, loff_t offset, size_t *size,
+                              unsigned long limit)
+{
+       const struct elf_phdr *const gate_phdrs =
+               (const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);
+       int i;
+       Elf64_Off ofs = 0;
+
+       for (i = 0; i < GATE_EHDR->e_phnum; ++i) {
+               struct elf_phdr phdr = gate_phdrs[i];
+
+               if (phdr.p_type == PT_LOAD) {
+                       phdr.p_memsz = PAGE_ALIGN(phdr.p_memsz);
+                       phdr.p_filesz = phdr.p_memsz;
+                       if (ofs == 0) {
+                               ofs = phdr.p_offset = offset;
+                               offset += phdr.p_filesz;
+                       } else {
+                               phdr.p_offset = ofs;
+                       }
+               } else {
+                       phdr.p_offset += ofs;
+               }
+               phdr.p_paddr = 0; /* match other core phdrs */
+               *size += sizeof(phdr);
+               if (*size > limit || !dump_write(file, &phdr, sizeof(phdr)))
+                       return 0;
+       }
+       return 1;
+}
+
+int elf_core_write_extra_data(struct file *file, size_t *size,
+                             unsigned long limit)
+{
+       const struct elf_phdr *const gate_phdrs =
+               (const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);
+       int i;
+
+       for (i = 0; i < GATE_EHDR->e_phnum; ++i) {
+               if (gate_phdrs[i].p_type == PT_LOAD) {
+                       void *addr = (void *)gate_phdrs[i].p_vaddr;
+                       size_t memsz = PAGE_ALIGN(gate_phdrs[i].p_memsz);
+
+                       *size += memsz;
+                       if (*size > limit || !dump_write(file, addr, memsz))
+                               return 0;
+                       break;
+               }
+       }
+       return 1;
+}
+
+size_t elf_core_extra_data_size(void)
+{
+       const struct elf_phdr *const gate_phdrs =
+               (const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);
+       int i;
+       size_t size = 0;
+
+       for (i = 0; i < GATE_EHDR->e_phnum; ++i) {
+               if (gate_phdrs[i].p_type == PT_LOAD) {
+                       size += PAGE_ALIGN(gate_phdrs[i].p_memsz);
+                       break;
+               }
+       }
+       return size;
+}
index b81e46b1629b2a0cfe12e40eac2426c33b33a4e1..703062c44fb9df33c7eefceefcf82b9598c71c59 100644 (file)
@@ -2315,6 +2315,7 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t
                DPRINT(("Cannot allocate vma\n"));
                goto error_kmem;
        }
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
 
        /*
         * partially initialize the vma for the sampling buffer
index 01c75797119c568bd4d16919d3a122354a8b1a9c..fa4d1e59deb05c21281011cc07fe5fecfd8a3fb4 100644 (file)
@@ -26,6 +26,7 @@ config KVM
        select ANON_INODES
        select HAVE_KVM_IRQCHIP
        select KVM_APIC_ARCHITECTURE
+       select KVM_MMIO
        ---help---
          Support hosting fully virtualized guest machines using hardware
          virtualization extensions.  You will need a fairly recent
index 5fdeec5fddcf60db5d6a96dd5f5d86e533f2b0f1..26e0e089bfe76b0b89772bfc7f5617abc02d2a9d 100644 (file)
@@ -241,10 +241,10 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        return 0;
 mmio:
        if (p->dir)
-               r = kvm_io_bus_read(&vcpu->kvm->mmio_bus, p->addr,
+               r = kvm_io_bus_read(vcpu->kvm, KVM_MMIO_BUS, p->addr,
                                    p->size, &p->data);
        else
-               r = kvm_io_bus_write(&vcpu->kvm->mmio_bus, p->addr,
+               r = kvm_io_bus_write(vcpu->kvm, KVM_MMIO_BUS, p->addr,
                                     p->size, &p->data);
        if (r)
                printk(KERN_ERR"kvm: No iodevice found! addr:%lx\n", p->addr);
@@ -636,12 +636,9 @@ static void kvm_vcpu_post_transition(struct kvm_vcpu *vcpu)
 static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
        union context *host_ctx, *guest_ctx;
-       int r;
+       int r, idx;
 
-       /*
-        * down_read() may sleep and return with interrupts enabled
-        */
-       down_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
 
 again:
        if (signal_pending(current)) {
@@ -663,7 +660,7 @@ again:
        if (r < 0)
                goto vcpu_run_fail;
 
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
        kvm_guest_enter();
 
        /*
@@ -687,7 +684,7 @@ again:
        kvm_guest_exit();
        preempt_enable();
 
-       down_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
 
        r = kvm_handle_exit(kvm_run, vcpu);
 
@@ -697,10 +694,10 @@ again:
        }
 
 out:
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
        if (r > 0) {
                kvm_resched(vcpu);
-               down_read(&vcpu->kvm->slots_lock);
+               idx = srcu_read_lock(&vcpu->kvm->srcu);
                goto again;
        }
 
@@ -971,7 +968,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
                        goto out;
                r = kvm_setup_default_irq_routing(kvm);
                if (r) {
-                       kfree(kvm->arch.vioapic);
+                       kvm_ioapic_destroy(kvm);
                        goto out;
                }
                break;
@@ -1377,12 +1374,14 @@ static void free_kvm(struct kvm *kvm)
 
 static void kvm_release_vm_pages(struct kvm *kvm)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
        int i, j;
        unsigned long base_gfn;
 
-       for (i = 0; i < kvm->nmemslots; i++) {
-               memslot = &kvm->memslots[i];
+       slots = rcu_dereference(kvm->memslots);
+       for (i = 0; i < slots->nmemslots; i++) {
+               memslot = &slots->memslots[i];
                base_gfn = memslot->base_gfn;
 
                for (j = 0; j < memslot->npages; j++) {
@@ -1405,6 +1404,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        kfree(kvm->arch.vioapic);
        kvm_release_vm_pages(kvm);
        kvm_free_physmem(kvm);
+       cleanup_srcu_struct(&kvm->srcu);
        free_kvm(kvm);
 }
 
@@ -1576,15 +1576,15 @@ out:
        return r;
 }
 
-int kvm_arch_set_memory_region(struct kvm *kvm,
-               struct kvm_userspace_memory_region *mem,
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+               struct kvm_memory_slot *memslot,
                struct kvm_memory_slot old,
+               struct kvm_userspace_memory_region *mem,
                int user_alloc)
 {
        unsigned long i;
        unsigned long pfn;
-       int npages = mem->memory_size >> PAGE_SHIFT;
-       struct kvm_memory_slot *memslot = &kvm->memslots[mem->slot];
+       int npages = memslot->npages;
        unsigned long base_gfn = memslot->base_gfn;
 
        if (base_gfn + npages > (KVM_MAX_MEM_SIZE >> PAGE_SHIFT))
@@ -1608,6 +1608,14 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
        return 0;
 }
 
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+               struct kvm_userspace_memory_region *mem,
+               struct kvm_memory_slot old,
+               int user_alloc)
+{
+       return;
+}
+
 void kvm_arch_flush_shadow(struct kvm *kvm)
 {
        kvm_flush_remote_tlbs(kvm);
@@ -1802,7 +1810,7 @@ static int kvm_ia64_sync_dirty_log(struct kvm *kvm,
        if (log->slot >= KVM_MEMORY_SLOTS)
                goto out;
 
-       memslot = &kvm->memslots[log->slot];
+       memslot = &kvm->memslots->memslots[log->slot];
        r = -ENOENT;
        if (!memslot->dirty_bitmap)
                goto out;
@@ -1827,6 +1835,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
        struct kvm_memory_slot *memslot;
        int is_dirty = 0;
 
+       mutex_lock(&kvm->slots_lock);
        spin_lock(&kvm->arch.dirty_log_lock);
 
        r = kvm_ia64_sync_dirty_log(kvm, log);
@@ -1840,12 +1849,13 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
        /* If nothing is dirty, don't bother messing with page tables. */
        if (is_dirty) {
                kvm_flush_remote_tlbs(kvm);
-               memslot = &kvm->memslots[log->slot];
+               memslot = &kvm->memslots->memslots[log->slot];
                n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;
                memset(memslot->dirty_bitmap, 0, n);
        }
        r = 0;
 out:
+       mutex_unlock(&kvm->slots_lock);
        spin_unlock(&kvm->arch.dirty_log_lock);
        return r;
 }
index e4b82319881db227044550b3330f1eea69770f50..cb548ee9fcaed4ce24c1f3c0994d397944afbe9d 100644 (file)
@@ -75,7 +75,7 @@ static void set_pal_result(struct kvm_vcpu *vcpu,
        struct exit_ctl_data *p;
 
        p = kvm_get_exit_data(vcpu);
-       if (p && p->exit_reason == EXIT_REASON_PAL_CALL) {
+       if (p->exit_reason == EXIT_REASON_PAL_CALL) {
                p->u.pal_data.ret = result;
                return ;
        }
@@ -87,7 +87,7 @@ static void set_sal_result(struct kvm_vcpu *vcpu,
        struct exit_ctl_data *p;
 
        p = kvm_get_exit_data(vcpu);
-       if (p && p->exit_reason == EXIT_REASON_SAL_CALL) {
+       if (p->exit_reason == EXIT_REASON_SAL_CALL) {
                p->u.sal_data.ret = result;
                return ;
        }
@@ -322,7 +322,7 @@ static  u64 kvm_get_pal_call_index(struct kvm_vcpu *vcpu)
        struct exit_ctl_data *p;
 
        p = kvm_get_exit_data(vcpu);
-       if (p && (p->exit_reason == EXIT_REASON_PAL_CALL))
+       if (p->exit_reason == EXIT_REASON_PAL_CALL)
                index = p->u.pal_data.gr28;
 
        return index;
@@ -646,18 +646,16 @@ static void kvm_get_sal_call_data(struct kvm_vcpu *vcpu, u64 *in0, u64 *in1,
 
        p = kvm_get_exit_data(vcpu);
 
-       if (p) {
-               if (p->exit_reason == EXIT_REASON_SAL_CALL) {
-                       *in0 = p->u.sal_data.in0;
-                       *in1 = p->u.sal_data.in1;
-                       *in2 = p->u.sal_data.in2;
-                       *in3 = p->u.sal_data.in3;
-                       *in4 = p->u.sal_data.in4;
-                       *in5 = p->u.sal_data.in5;
-                       *in6 = p->u.sal_data.in6;
-                       *in7 = p->u.sal_data.in7;
-                       return ;
-               }
+       if (p->exit_reason == EXIT_REASON_SAL_CALL) {
+               *in0 = p->u.sal_data.in0;
+               *in1 = p->u.sal_data.in1;
+               *in2 = p->u.sal_data.in2;
+               *in3 = p->u.sal_data.in3;
+               *in4 = p->u.sal_data.in4;
+               *in5 = p->u.sal_data.in5;
+               *in6 = p->u.sal_data.in6;
+               *in7 = p->u.sal_data.in7;
+               return ;
        }
        *in0 = 0;
 }
index 9bf55afd08d0603902ae63877499a91488a6d221..fb8f9f59a1eddea3bf967f5475582f5783980f9e 100644 (file)
@@ -316,8 +316,8 @@ void emulate_io_inst(struct kvm_vcpu *vcpu, u64 padr, u64 ma)
                return;
        } else {
                inst_type = -1;
-               panic_vm(vcpu, "Unsupported MMIO access instruction! \
-                               Bunld[0]=0x%lx, Bundle[1]=0x%lx\n",
+               panic_vm(vcpu, "Unsupported MMIO access instruction! "
+                               "Bunld[0]=0x%lx, Bundle[1]=0x%lx\n",
                                bundle.i64[0], bundle.i64[1]);
        }
 
index dce75b70cdd5d9abce6b28d419b5aba6ba97cb0d..958815c9787d2997a64ba4636084e0676fdc30ea 100644 (file)
@@ -1639,8 +1639,8 @@ void vcpu_set_psr(struct kvm_vcpu *vcpu, unsigned long val)
         * Otherwise panic
         */
        if (val & (IA64_PSR_PK | IA64_PSR_IS | IA64_PSR_VM))
-               panic_vm(vcpu, "Only support guests with vpsr.pk =0 \
-                               & vpsr.is=0\n");
+               panic_vm(vcpu, "Only support guests with vpsr.pk =0 "
+                               "& vpsr.is=0\n");
 
        /*
         * For those IA64_PSR bits: id/da/dd/ss/ed/ia
index ca3335ea56ccf9dae52d19ddbe619315f1134225..ed41759efcac3b76e44d9a6b87c0c379448b4c5c 100644 (file)
@@ -117,6 +117,7 @@ ia64_init_addr_space (void)
         */
        vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
        if (vma) {
+               INIT_LIST_HEAD(&vma->anon_vma_chain);
                vma->vm_mm = current->mm;
                vma->vm_start = current->thread.rbs_bot & PAGE_MASK;
                vma->vm_end = vma->vm_start + PAGE_SIZE;
@@ -135,6 +136,7 @@ ia64_init_addr_space (void)
        if (!(current->personality & MMAP_PAGE_ZERO)) {
                vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
                if (vma) {
+                       INIT_LIST_HEAD(&vma->anon_vma_chain);
                        vma->vm_mm = current->mm;
                        vma->vm_end = PAGE_SIZE;
                        vma->vm_page_prot = __pgprot(pgprot_val(PAGE_READONLY) | _PAGE_MA_NAT);
index bc989e522a045c17ca4514478b7cb5ef9d77fc4d..7305ac8f7f5ba7438ac2207ada7aaeb60b419d36 100644 (file)
@@ -12,4 +12,18 @@ config DEBUG_RODATA
          portion of the kernel code won't be covered by a TLB anymore.
          If in doubt, say "N".
 
+config DEBUG_STRICT_USER_COPY_CHECKS
+       bool "Strict copy size checks"
+       depends on DEBUG_KERNEL && !TRACE_BRANCH_PROFILING
+       ---help---
+         Enabling this option turns a certain set of sanity checks for user
+         copy operations into compile time failures.
+
+         The copy_from_user() etc checks are there to help test if there
+         are sufficient security checks on the length argument of
+         the copy operation, by having gcc prove that the argument is
+         within bounds.
+
+         If unsure, or if you run an older (pre 4.4) gcc, say N.
+
 endmenu
index 32e03d8778587806cb001c9f6b98df3f1763e256..965d45427975907ae67efb45e20c8f500fd1ad3a 100644 (file)
@@ -1,22 +1 @@
-#ifndef _ASMPARISC_PARAM_H
-#define _ASMPARISC_PARAM_H
-
-#ifdef __KERNEL__
-#define HZ             CONFIG_HZ
-#define USER_HZ                100             /* some user API use "ticks" */
-#define CLOCKS_PER_SEC (USER_HZ)       /* like times() */
-#endif
-
-#ifndef HZ
-#define HZ 100
-#endif
-
-#define EXEC_PAGESIZE  4096
-
-#ifndef NOGROUP
-#define NOGROUP                (-1)
-#endif
-
-#define MAXHOSTNAMELEN 64      /* max length of hostname */
-
-#endif
+#include <asm-generic/param.h>
index d91357bca5b4cc18a3c143f948f24c7afef379cc..4653c77bf9d13398c6dd6777786a20c3a821c2dd 100644 (file)
@@ -160,7 +160,7 @@ static inline void set_eiem(unsigned long val)
    ldcd). */
 
 #define __PA_LDCW_ALIGNMENT    4
-#define __ldcw_align(a) ((volatile unsigned int *)a)
+#define __ldcw_align(a) (&(a)->slock)
 #define __LDCW "ldcw,co"
 
 #endif /*!CONFIG_PA20*/
index 7cf799d70b4c1b5da344895ddc6c055fe8d26686..ff4cf9dab8d226a74ce360c4a1ac7b91265ae96b 100644 (file)
@@ -7,6 +7,7 @@
 #include <asm/page.h>
 #include <asm/system.h>
 #include <asm/cache.h>
+#include <asm/errno.h>
 #include <asm-generic/uaccess-unaligned.h>
 
 #define VERIFY_READ 0
@@ -234,13 +235,35 @@ extern long lstrnlen_user(const char __user *,long);
 
 unsigned long copy_to_user(void __user *dst, const void *src, unsigned long len);
 #define __copy_to_user copy_to_user
-unsigned long copy_from_user(void *dst, const void __user *src, unsigned long len);
-#define __copy_from_user copy_from_user
+unsigned long __copy_from_user(void *dst, const void __user *src, unsigned long len);
 unsigned long copy_in_user(void __user *dst, const void __user *src, unsigned long len);
 #define __copy_in_user copy_in_user
 #define __copy_to_user_inatomic __copy_to_user
 #define __copy_from_user_inatomic __copy_from_user
 
+extern void copy_from_user_overflow(void)
+#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS
+        __compiletime_error("copy_from_user() buffer size is not provably correct")
+#else
+        __compiletime_warning("copy_from_user() buffer size is not provably correct")
+#endif
+;
+
+static inline unsigned long __must_check copy_from_user(void *to,
+                                          const void __user *from,
+                                          unsigned long n)
+{
+        int sz = __compiletime_object_size(to);
+        int ret = -EFAULT;
+
+        if (likely(sz == -1 || !__builtin_constant_p(n) || sz >= n))
+                ret = __copy_from_user(to, from, n);
+        else
+                copy_from_user_overflow();
+
+        return ret;
+}
+
 struct pt_regs;
 int fixup_exception(struct pt_regs *regs);
 
index cda158318c6204341272d13f6887fd2a799ec162..1ce7d2851d90b9dffb821008c588fee1d9e43ebd 100644 (file)
 #define __NR_pwritev           (__NR_Linux + 316)
 #define __NR_rt_tgsigqueueinfo (__NR_Linux + 317)
 #define __NR_perf_event_open   (__NR_Linux + 318)
+#define __NR_recvmmsg          (__NR_Linux + 319)
+#define __NR_accept4           (__NR_Linux + 320)
 
-#define __NR_Linux_syscalls    (__NR_perf_event_open + 1)
+#define __NR_Linux_syscalls    (__NR_accept4 + 1)
 
 
 #define __IGNORE_select                /* newselect */
index 1054baa2fc69000a17faa4bca451864cbdac2f0d..d054f3da3ff512fa07c0a5b5b818a8e53adfcada 100644 (file)
@@ -171,14 +171,14 @@ parisc_cache_init(void)
                cache_info.ic_conf.cc_cst,
                cache_info.ic_conf.cc_hv);
 
-       printk("D-TLB conf: sh %d page %d cst %d aid %d pad1 %d \n",
+       printk("D-TLB conf: sh %d page %d cst %d aid %d pad1 %d\n",
                cache_info.dt_conf.tc_sh,
                cache_info.dt_conf.tc_page,
                cache_info.dt_conf.tc_cst,
                cache_info.dt_conf.tc_aid,
                cache_info.dt_conf.tc_pad1);
 
-       printk("I-TLB conf: sh %d page %d cst %d aid %d pad1 %d \n",
+       printk("I-TLB conf: sh %d page %d cst %d aid %d pad1 %d\n",
                cache_info.it_conf.tc_sh,
                cache_info.it_conf.tc_page,
                cache_info.it_conf.tc_cst,
index 01c4fcf8f48160a8f176a7a761029af6fb674c93..de5f6dab48b70564b72349e0aa2106d9b80937eb 100644 (file)
        ENTRY_COMP(pwritev)
        ENTRY_COMP(rt_tgsigqueueinfo)
        ENTRY_SAME(perf_event_open)
+       ENTRY_COMP(recvmmsg)
+       ENTRY_SAME(accept4)             /* 320 */
 
        /* Nothing yet */
 
index a79c6f9e7e2c441f0d2d7feda117ae14c72ad065..05511ccb61d24a9bc8735f9897537e3bd520b825 100644 (file)
@@ -250,9 +250,21 @@ static int __init rtc_init(void)
 }
 module_init(rtc_init);
 
-void __init time_init(void)
+void read_persistent_clock(struct timespec *ts)
 {
        static struct pdc_tod tod_data;
+       if (pdc_tod_read(&tod_data) == 0) {
+               ts->tv_sec = tod_data.tod_sec;
+               ts->tv_nsec = tod_data.tod_usec * 1000;
+       } else {
+               printk(KERN_ERR "Error reading tod clock\n");
+               ts->tv_sec = 0;
+               ts->tv_nsec = 0;
+       }
+}
+
+void __init time_init(void)
+{
        unsigned long current_cr16_khz;
 
        clocktick = (100 * PAGE0->mem_10msec) / HZ;
@@ -264,19 +276,4 @@ void __init time_init(void)
        clocksource_cr16.mult = clocksource_khz2mult(current_cr16_khz,
                                                clocksource_cr16.shift);
        clocksource_register(&clocksource_cr16);
-
-       if (pdc_tod_read(&tod_data) == 0) {
-               unsigned long flags;
-
-               write_seqlock_irqsave(&xtime_lock, flags);
-               xtime.tv_sec = tod_data.tod_sec;
-               xtime.tv_nsec = tod_data.tod_usec * 1000;
-               set_normalized_timespec(&wall_to_monotonic,
-                                       -xtime.tv_sec, -xtime.tv_nsec);
-               write_sequnlock_irqrestore(&xtime_lock, flags);
-       } else {
-               printk(KERN_ERR "Error reading tod clock\n");
-               xtime.tv_sec = 0;
-               xtime.tv_nsec = 0;
-       }
 }
index e6f4b7a4b7e321eaefbd03dc5b2766f55f73d887..92d977bb5ea89839c9534d19d1f666ec3d69104f 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
+#include <linux/ratelimit.h>
 #include <asm/uaccess.h>
 
 /* #define DEBUG_UNALIGNED 1 */
@@ -446,8 +447,7 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
 
 void handle_unaligned(struct pt_regs *regs)
 {
-       static unsigned long unaligned_count = 0;
-       static unsigned long last_time = 0;
+       static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
        unsigned long newbase = R1(regs->iir)?regs->gr[R1(regs->iir)]:0;
        int modify = 0;
        int ret = ERR_NOTHANDLED;
@@ -460,14 +460,8 @@ void handle_unaligned(struct pt_regs *regs)
                        goto force_sigbus;
                }
 
-               if (unaligned_count > 5 &&
-                               time_after(jiffies, last_time + 5 * HZ)) {
-                       unaligned_count = 0;
-                       last_time = jiffies;
-               }
-
-               if (!(current->thread.flags & PARISC_UAC_NOPRINT) 
-                   && ++unaligned_count < 5) {
+               if (!(current->thread.flags & PARISC_UAC_NOPRINT) &&
+                       __ratelimit(&ratelimit)) {
                        char buf[256];
                        sprintf(buf, "%s(%d): unaligned access to 0x" RFMT " at ip=0x" RFMT "\n",
                                current->comm, task_pid_nr(current), regs->ior, regs->iaoq[0]);
index abf41f4632a9fca1b8df2c4ba963c2e328e553e7..1dbca5c31b3c44ee0eff1d35ef0b2c7e6336fc4f 100644 (file)
@@ -475,7 +475,8 @@ unsigned long copy_to_user(void __user *dst, const void *src, unsigned long len)
        return pa_memcpy((void __force *)dst, src, len);
 }
 
-unsigned long copy_from_user(void *dst, const void __user *src, unsigned long len)
+EXPORT_SYMBOL(__copy_from_user);
+unsigned long __copy_from_user(void *dst, const void __user *src, unsigned long len)
 {
        mtsp(get_user_space(), 1);
        mtsp(get_kernel_space(), 2);
index af2abe74f54440199fdccfeccdd2de085ec7a203..aadf2dd6f84e15c919c9020e9271c3e8f7427a35 100644 (file)
 #define RESUME_HOST             RESUME_FLAG_HOST
 #define RESUME_HOST_NV          (RESUME_FLAG_HOST|RESUME_FLAG_NV)
 
+#define KVM_GUEST_MODE_NONE    0
+#define KVM_GUEST_MODE_GUEST   1
+#define KVM_GUEST_MODE_SKIP    2
+
+#define KVM_INST_FETCH_FAILED  -1
+
 #endif /* __POWERPC_KVM_ASM_H__ */
index 74b7369770d05b3f93a9c233e3815aef3ad51300..db7db0a96967ad8dc4c2eb2a5c2438d878187e73 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <linux/types.h>
 #include <linux/kvm_host.h>
-#include <asm/kvm_ppc.h>
+#include <asm/kvm_book3s_64_asm.h>
 
 struct kvmppc_slb {
        u64 esid;
@@ -33,7 +33,8 @@ struct kvmppc_slb {
        bool Ks;
        bool Kp;
        bool nx;
-       bool large;
+       bool large;     /* PTEs are 16MB */
+       bool tb;        /* 1TB segment */
        bool class;
 };
 
@@ -69,6 +70,7 @@ struct kvmppc_sid_map {
 
 struct kvmppc_vcpu_book3s {
        struct kvm_vcpu vcpu;
+       struct kvmppc_book3s_shadow_vcpu shadow_vcpu;
        struct kvmppc_sid_map sid_map[SID_MAP_NUM];
        struct kvmppc_slb slb[64];
        struct {
@@ -89,6 +91,7 @@ struct kvmppc_vcpu_book3s {
        u64 vsid_next;
        u64 vsid_max;
        int context_id;
+       ulong prog_flags; /* flags to inject when giving a 700 trap */
 };
 
 #define CONTEXT_HOST           0
@@ -119,6 +122,10 @@ extern void kvmppc_set_bat(struct kvm_vcpu *vcpu, struct kvmppc_bat *bat,
 
 extern u32 kvmppc_trampoline_lowmem;
 extern u32 kvmppc_trampoline_enter;
+extern void kvmppc_rmcall(ulong srr0, ulong srr1);
+extern void kvmppc_load_up_fpu(void);
+extern void kvmppc_load_up_altivec(void);
+extern void kvmppc_load_up_vsx(void);
 
 static inline struct kvmppc_vcpu_book3s *to_book3s(struct kvm_vcpu *vcpu)
 {
index 2e06ee8184ef8caef068ae2fc4496cb3404cc13c..183461b484076e25fd5ca714cce4426b82b0e1e6 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef __ASM_KVM_BOOK3S_ASM_H__
 #define __ASM_KVM_BOOK3S_ASM_H__
 
+#ifdef __ASSEMBLY__
+
 #ifdef CONFIG_KVM_BOOK3S_64_HANDLER
 
 #include <asm/kvm_asm.h>
@@ -55,4 +57,20 @@ kvmppc_resume_\intno:
 
 #endif /* CONFIG_KVM_BOOK3S_64_HANDLER */
 
+#else  /*__ASSEMBLY__ */
+
+struct kvmppc_book3s_shadow_vcpu {
+       ulong gpr[14];
+       u32 cr;
+       u32 xer;
+       ulong host_r1;
+       ulong host_r2;
+       ulong handler;
+       ulong scratch0;
+       ulong scratch1;
+       ulong vmhandler;
+};
+
+#endif /*__ASSEMBLY__ */
+
 #endif /* __ASM_KVM_BOOK3S_ASM_H__ */
index 9d497ce497267fa8958add05cfa0aa5d211af903..7fea26fffb25f692f50f71d8e4b3d858d9478aad 100644 (file)
@@ -52,9 +52,12 @@ struct kvmppc_vcpu_e500 {
        u32 mas5;
        u32 mas6;
        u32 mas7;
+       u32 l1csr0;
        u32 l1csr1;
        u32 hid0;
        u32 hid1;
+       u32 tlb0cfg;
+       u32 tlb1cfg;
 
        struct kvm_vcpu vcpu;
 };
index 1201f62d0d73f23e2e75e9b9622499ca259359c7..5e5bae7e152fe4a084eef9f61cf72abfe728e399 100644 (file)
@@ -167,23 +167,40 @@ struct kvm_vcpu_arch {
        ulong trampoline_lowmem;
        ulong trampoline_enter;
        ulong highmem_handler;
+       ulong rmcall;
        ulong host_paca_phys;
        struct kvmppc_mmu mmu;
 #endif
 
-       u64 fpr[32];
        ulong gpr[32];
 
+       u64 fpr[32];
+       u32 fpscr;
+
+#ifdef CONFIG_ALTIVEC
+       vector128 vr[32];
+       vector128 vscr;
+#endif
+
+#ifdef CONFIG_VSX
+       u64 vsr[32];
+#endif
+
        ulong pc;
-       u32 cr;
        ulong ctr;
        ulong lr;
+
+#ifdef CONFIG_BOOKE
        ulong xer;
+       u32 cr;
+#endif
 
        ulong msr;
 #ifdef CONFIG_PPC64
        ulong shadow_msr;
+       ulong shadow_srr1;
        ulong hflags;
+       ulong guest_owned_ext;
 #endif
        u32 mmucr;
        ulong sprg0;
@@ -242,6 +259,8 @@ struct kvm_vcpu_arch {
 #endif
        ulong fault_dear;
        ulong fault_esr;
+       ulong queued_dear;
+       ulong queued_esr;
        gpa_t paddr_accessed;
 
        u8 io_gpr; /* GPR used as IO source/target */
index 269ee46ab0285701f84411748a51e6f14c5b1018..e2642829e4350b5d9c0f62a17c11b03a06f75c58 100644 (file)
@@ -28,6 +28,9 @@
 #include <linux/types.h>
 #include <linux/kvm_types.h>
 #include <linux/kvm_host.h>
+#ifdef CONFIG_PPC_BOOK3S
+#include <asm/kvm_book3s.h>
+#endif
 
 enum emulation_result {
        EMULATE_DONE,         /* no further processing */
@@ -80,8 +83,9 @@ extern void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu);
 
 extern void kvmppc_core_deliver_interrupts(struct kvm_vcpu *vcpu);
 extern int kvmppc_core_pending_dec(struct kvm_vcpu *vcpu);
-extern void kvmppc_core_queue_program(struct kvm_vcpu *vcpu);
+extern void kvmppc_core_queue_program(struct kvm_vcpu *vcpu, ulong flags);
 extern void kvmppc_core_queue_dec(struct kvm_vcpu *vcpu);
+extern void kvmppc_core_dequeue_dec(struct kvm_vcpu *vcpu);
 extern void kvmppc_core_queue_external(struct kvm_vcpu *vcpu,
                                        struct kvm_interrupt *irq);
 
@@ -95,4 +99,81 @@ extern void kvmppc_booke_exit(void);
 
 extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu);
 
+#ifdef CONFIG_PPC_BOOK3S
+
+/* We assume we're always acting on the current vcpu */
+
+static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
+{
+       if ( num < 14 ) {
+               get_paca()->shadow_vcpu.gpr[num] = val;
+               to_book3s(vcpu)->shadow_vcpu.gpr[num] = val;
+       } else
+               vcpu->arch.gpr[num] = val;
+}
+
+static inline ulong kvmppc_get_gpr(struct kvm_vcpu *vcpu, int num)
+{
+       if ( num < 14 )
+               return get_paca()->shadow_vcpu.gpr[num];
+       else
+               return vcpu->arch.gpr[num];
+}
+
+static inline void kvmppc_set_cr(struct kvm_vcpu *vcpu, u32 val)
+{
+       get_paca()->shadow_vcpu.cr = val;
+       to_book3s(vcpu)->shadow_vcpu.cr = val;
+}
+
+static inline u32 kvmppc_get_cr(struct kvm_vcpu *vcpu)
+{
+       return get_paca()->shadow_vcpu.cr;
+}
+
+static inline void kvmppc_set_xer(struct kvm_vcpu *vcpu, u32 val)
+{
+       get_paca()->shadow_vcpu.xer = val;
+       to_book3s(vcpu)->shadow_vcpu.xer = val;
+}
+
+static inline u32 kvmppc_get_xer(struct kvm_vcpu *vcpu)
+{
+       return get_paca()->shadow_vcpu.xer;
+}
+
+#else
+
+static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val)
+{
+       vcpu->arch.gpr[num] = val;
+}
+
+static inline ulong kvmppc_get_gpr(struct kvm_vcpu *vcpu, int num)
+{
+       return vcpu->arch.gpr[num];
+}
+
+static inline void kvmppc_set_cr(struct kvm_vcpu *vcpu, u32 val)
+{
+       vcpu->arch.cr = val;
+}
+
+static inline u32 kvmppc_get_cr(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.cr;
+}
+
+static inline void kvmppc_set_xer(struct kvm_vcpu *vcpu, u32 val)
+{
+       vcpu->arch.xer = val;
+}
+
+static inline u32 kvmppc_get_xer(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.xer;
+}
+
+#endif
+
 #endif /* __POWERPC_KVM_PPC_H__ */
index 5e9b4ef71415c70b207769b8a9caa1ff486eea71..d8a693109c8294808c226ec57b8d75e4ed57a92d 100644 (file)
@@ -19,6 +19,9 @@
 #include <asm/mmu.h>
 #include <asm/page.h>
 #include <asm/exception-64e.h>
+#ifdef CONFIG_KVM_BOOK3S_64_HANDLER
+#include <asm/kvm_book3s_64_asm.h>
+#endif
 
 register struct paca_struct *local_paca asm("r13");
 
@@ -135,6 +138,8 @@ struct paca_struct {
                u64     esid;
                u64     vsid;
        } kvm_slb[64];                  /* guest SLB */
+       /* We use this to store guest state in */
+       struct kvmppc_book3s_shadow_vcpu shadow_vcpu;
        u8 kvm_slb_max;                 /* highest used guest slb entry */
        u8 kvm_in_guest;                /* are we inside the guest? */
 #endif
index bc8dd53f718a1b201a16e91f4864bb221f5acb25..5572e86223f4f5afcf0634063d3279391fb1fa10 100644 (file)
 #define   SRR1_WAKEMT          0x00280000 /* mtctrl */
 #define   SRR1_WAKEDEC         0x00180000 /* Decrementer interrupt */
 #define   SRR1_WAKETHERM       0x00100000 /* Thermal management interrupt */
+#define   SRR1_PROGFPE         0x00100000 /* Floating Point Enabled */
+#define   SRR1_PROGPRIV                0x00040000 /* Privileged instruction */
+#define   SRR1_PROGTRAP                0x00020000 /* Trap */
+#define   SRR1_PROGADDR                0x00010000 /* SRR0 contains subsequent addr */
 #define SPRN_HSRR0     0x13A   /* Save/Restore Register 0 */
 #define SPRN_HSRR1     0x13B   /* Save/Restore Register 1 */
 
index a6c2b63227b32aa8b891ce33b5c3b8acc8787be0..957ceb7059c57a8a2bd7c5ffd9330a361baaf160 100644 (file)
@@ -194,6 +194,30 @@ int main(void)
        DEFINE(PACA_KVM_IN_GUEST, offsetof(struct paca_struct, kvm_in_guest));
        DEFINE(PACA_KVM_SLB, offsetof(struct paca_struct, kvm_slb));
        DEFINE(PACA_KVM_SLB_MAX, offsetof(struct paca_struct, kvm_slb_max));
+       DEFINE(PACA_KVM_CR, offsetof(struct paca_struct, shadow_vcpu.cr));
+       DEFINE(PACA_KVM_XER, offsetof(struct paca_struct, shadow_vcpu.xer));
+       DEFINE(PACA_KVM_R0, offsetof(struct paca_struct, shadow_vcpu.gpr[0]));
+       DEFINE(PACA_KVM_R1, offsetof(struct paca_struct, shadow_vcpu.gpr[1]));
+       DEFINE(PACA_KVM_R2, offsetof(struct paca_struct, shadow_vcpu.gpr[2]));
+       DEFINE(PACA_KVM_R3, offsetof(struct paca_struct, shadow_vcpu.gpr[3]));
+       DEFINE(PACA_KVM_R4, offsetof(struct paca_struct, shadow_vcpu.gpr[4]));
+       DEFINE(PACA_KVM_R5, offsetof(struct paca_struct, shadow_vcpu.gpr[5]));
+       DEFINE(PACA_KVM_R6, offsetof(struct paca_struct, shadow_vcpu.gpr[6]));
+       DEFINE(PACA_KVM_R7, offsetof(struct paca_struct, shadow_vcpu.gpr[7]));
+       DEFINE(PACA_KVM_R8, offsetof(struct paca_struct, shadow_vcpu.gpr[8]));
+       DEFINE(PACA_KVM_R9, offsetof(struct paca_struct, shadow_vcpu.gpr[9]));
+       DEFINE(PACA_KVM_R10, offsetof(struct paca_struct, shadow_vcpu.gpr[10]));
+       DEFINE(PACA_KVM_R11, offsetof(struct paca_struct, shadow_vcpu.gpr[11]));
+       DEFINE(PACA_KVM_R12, offsetof(struct paca_struct, shadow_vcpu.gpr[12]));
+       DEFINE(PACA_KVM_R13, offsetof(struct paca_struct, shadow_vcpu.gpr[13]));
+       DEFINE(PACA_KVM_HOST_R1, offsetof(struct paca_struct, shadow_vcpu.host_r1));
+       DEFINE(PACA_KVM_HOST_R2, offsetof(struct paca_struct, shadow_vcpu.host_r2));
+       DEFINE(PACA_KVM_VMHANDLER, offsetof(struct paca_struct,
+                                           shadow_vcpu.vmhandler));
+       DEFINE(PACA_KVM_SCRATCH0, offsetof(struct paca_struct,
+                                          shadow_vcpu.scratch0));
+       DEFINE(PACA_KVM_SCRATCH1, offsetof(struct paca_struct,
+                                          shadow_vcpu.scratch1));
 #endif
 #endif /* CONFIG_PPC64 */
 
@@ -389,8 +413,6 @@ int main(void)
        DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid));
        DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr));
        DEFINE(VCPU_LR, offsetof(struct kvm_vcpu, arch.lr));
-       DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr));
-       DEFINE(VCPU_XER, offsetof(struct kvm_vcpu, arch.xer));
        DEFINE(VCPU_CTR, offsetof(struct kvm_vcpu, arch.ctr));
        DEFINE(VCPU_PC, offsetof(struct kvm_vcpu, arch.pc));
        DEFINE(VCPU_MSR, offsetof(struct kvm_vcpu, arch.msr));
@@ -411,11 +433,16 @@ int main(void)
        DEFINE(VCPU_HOST_R2, offsetof(struct kvm_vcpu, arch.host_r2));
        DEFINE(VCPU_HOST_MSR, offsetof(struct kvm_vcpu, arch.host_msr));
        DEFINE(VCPU_SHADOW_MSR, offsetof(struct kvm_vcpu, arch.shadow_msr));
+       DEFINE(VCPU_SHADOW_SRR1, offsetof(struct kvm_vcpu, arch.shadow_srr1));
        DEFINE(VCPU_TRAMPOLINE_LOWMEM, offsetof(struct kvm_vcpu, arch.trampoline_lowmem));
        DEFINE(VCPU_TRAMPOLINE_ENTER, offsetof(struct kvm_vcpu, arch.trampoline_enter));
        DEFINE(VCPU_HIGHMEM_HANDLER, offsetof(struct kvm_vcpu, arch.highmem_handler));
+       DEFINE(VCPU_RMCALL, offsetof(struct kvm_vcpu, arch.rmcall));
        DEFINE(VCPU_HFLAGS, offsetof(struct kvm_vcpu, arch.hflags));
-#endif
+#else
+       DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr));
+       DEFINE(VCPU_XER, offsetof(struct kvm_vcpu, arch.xer));
+#endif /* CONFIG_PPC64 */
 #endif
 #ifdef CONFIG_44x
        DEFINE(PGD_T_LOG2, PGD_T_LOG2);
index 425451453e96d9cd7e14c6f20fdc6c852ec2f235..ab3e392ac63c41fda474ce4857660bbd38e459bd 100644 (file)
@@ -107,6 +107,7 @@ EXPORT_SYMBOL(giveup_altivec);
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_VSX
 EXPORT_SYMBOL(giveup_vsx);
+EXPORT_SYMBOL_GPL(__giveup_vsx);
 #endif /* CONFIG_VSX */
 #ifdef CONFIG_SPE
 EXPORT_SYMBOL(giveup_spe);
index 61af58fceceee8a6fcb87c9f0e2f1e55e9a3dfb5..65ea083a5b27bda5ba4cacb875894ec127513bde 100644 (file)
@@ -65,13 +65,14 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                         */
                        switch (dcrn) {
                        case DCRN_CPR0_CONFIG_ADDR:
-                               vcpu->arch.gpr[rt] = vcpu->arch.cpr0_cfgaddr;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.cpr0_cfgaddr);
                                break;
                        case DCRN_CPR0_CONFIG_DATA:
                                local_irq_disable();
                                mtdcr(DCRN_CPR0_CONFIG_ADDR,
                                          vcpu->arch.cpr0_cfgaddr);
-                               vcpu->arch.gpr[rt] = mfdcr(DCRN_CPR0_CONFIG_DATA);
+                               kvmppc_set_gpr(vcpu, rt,
+                                              mfdcr(DCRN_CPR0_CONFIG_DATA));
                                local_irq_enable();
                                break;
                        default:
@@ -93,11 +94,11 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        /* emulate some access in kernel */
                        switch (dcrn) {
                        case DCRN_CPR0_CONFIG_ADDR:
-                               vcpu->arch.cpr0_cfgaddr = vcpu->arch.gpr[rs];
+                               vcpu->arch.cpr0_cfgaddr = kvmppc_get_gpr(vcpu, rs);
                                break;
                        default:
                                run->dcr.dcrn = dcrn;
-                               run->dcr.data = vcpu->arch.gpr[rs];
+                               run->dcr.data = kvmppc_get_gpr(vcpu, rs);
                                run->dcr.is_write = 1;
                                vcpu->arch.dcr_needed = 1;
                                kvmppc_account_exit(vcpu, DCR_EXITS);
@@ -146,13 +147,13 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 
        switch (sprn) {
        case SPRN_PID:
-               kvmppc_set_pid(vcpu, vcpu->arch.gpr[rs]); break;
+               kvmppc_set_pid(vcpu, kvmppc_get_gpr(vcpu, rs)); break;
        case SPRN_MMUCR:
-               vcpu->arch.mmucr = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.mmucr = kvmppc_get_gpr(vcpu, rs); break;
        case SPRN_CCR0:
-               vcpu->arch.ccr0 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.ccr0 = kvmppc_get_gpr(vcpu, rs); break;
        case SPRN_CCR1:
-               vcpu->arch.ccr1 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.ccr1 = kvmppc_get_gpr(vcpu, rs); break;
        default:
                emulated = kvmppc_booke_emulate_mtspr(vcpu, sprn, rs);
        }
@@ -167,13 +168,13 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
 
        switch (sprn) {
        case SPRN_PID:
-               vcpu->arch.gpr[rt] = vcpu->arch.pid; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.pid); break;
        case SPRN_MMUCR:
-               vcpu->arch.gpr[rt] = vcpu->arch.mmucr; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.mmucr); break;
        case SPRN_CCR0:
-               vcpu->arch.gpr[rt] = vcpu->arch.ccr0; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ccr0); break;
        case SPRN_CCR1:
-               vcpu->arch.gpr[rt] = vcpu->arch.ccr1; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ccr1); break;
        default:
                emulated = kvmppc_booke_emulate_mfspr(vcpu, sprn, rt);
        }
index ff3cb63b8117f3e1285be25a0dce34a7194f1328..2570fcc7665ddd9376f2db851371efef80fa2045 100644 (file)
@@ -439,7 +439,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
        struct kvmppc_44x_tlbe *tlbe;
        unsigned int gtlb_index;
 
-       gtlb_index = vcpu->arch.gpr[ra];
+       gtlb_index = kvmppc_get_gpr(vcpu, ra);
        if (gtlb_index > KVM44x_GUEST_TLB_SIZE) {
                printk("%s: index %d\n", __func__, gtlb_index);
                kvmppc_dump_vcpu(vcpu);
@@ -455,15 +455,15 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
        switch (ws) {
        case PPC44x_TLB_PAGEID:
                tlbe->tid = get_mmucr_stid(vcpu);
-               tlbe->word0 = vcpu->arch.gpr[rs];
+               tlbe->word0 = kvmppc_get_gpr(vcpu, rs);
                break;
 
        case PPC44x_TLB_XLAT:
-               tlbe->word1 = vcpu->arch.gpr[rs];
+               tlbe->word1 = kvmppc_get_gpr(vcpu, rs);
                break;
 
        case PPC44x_TLB_ATTRIB:
-               tlbe->word2 = vcpu->arch.gpr[rs];
+               tlbe->word2 = kvmppc_get_gpr(vcpu, rs);
                break;
 
        default:
@@ -500,18 +500,20 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc)
        unsigned int as = get_mmucr_sts(vcpu);
        unsigned int pid = get_mmucr_stid(vcpu);
 
-       ea = vcpu->arch.gpr[rb];
+       ea = kvmppc_get_gpr(vcpu, rb);
        if (ra)
-               ea += vcpu->arch.gpr[ra];
+               ea += kvmppc_get_gpr(vcpu, ra);
 
        gtlb_index = kvmppc_44x_tlb_index(vcpu, ea, pid, as);
        if (rc) {
+               u32 cr = kvmppc_get_cr(vcpu);
+
                if (gtlb_index < 0)
-                       vcpu->arch.cr &= ~0x20000000;
+                       kvmppc_set_cr(vcpu, cr & ~0x20000000);
                else
-                       vcpu->arch.cr |= 0x20000000;
+                       kvmppc_set_cr(vcpu, cr | 0x20000000);
        }
-       vcpu->arch.gpr[rt] = gtlb_index;
+       kvmppc_set_gpr(vcpu, rt, gtlb_index);
 
        kvmppc_set_exit_type(vcpu, EMULATED_TLBSX_EXITS);
        return EMULATE_DONE;
index fe037fdaf1b392639a33030efd6b8a2190dc1ad4..60624cc9f4d4067a2c891e862a83b7c1a83f2de0 100644 (file)
@@ -20,6 +20,7 @@ config KVM
        bool
        select PREEMPT_NOTIFIERS
        select ANON_INODES
+       select KVM_MMIO
 
 config KVM_BOOK3S_64_HANDLER
        bool
index 3e294bd9b8c6d998ef262d013de4e02a3468fbca..9a271f0929c727c100fb0cda64dff969f020f49a 100644 (file)
 
 /* #define EXIT_DEBUG */
 /* #define EXIT_DEBUG_SIMPLE */
+/* #define DEBUG_EXT */
 
-/* Without AGGRESSIVE_DEC we only fire off a DEC interrupt when DEC turns 0.
- * When set, we retrigger a DEC interrupt after that if DEC <= 0.
- * PPC32 Linux runs faster without AGGRESSIVE_DEC, PPC64 Linux requires it. */
-
-/* #define AGGRESSIVE_DEC */
+static void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr);
 
 struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "exits",       VCPU_STAT(sum_exits) },
@@ -72,16 +69,24 @@ void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu)
 void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
        memcpy(get_paca()->kvm_slb, to_book3s(vcpu)->slb_shadow, sizeof(get_paca()->kvm_slb));
+       memcpy(&get_paca()->shadow_vcpu, &to_book3s(vcpu)->shadow_vcpu,
+              sizeof(get_paca()->shadow_vcpu));
        get_paca()->kvm_slb_max = to_book3s(vcpu)->slb_shadow_max;
 }
 
 void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
 {
        memcpy(to_book3s(vcpu)->slb_shadow, get_paca()->kvm_slb, sizeof(get_paca()->kvm_slb));
+       memcpy(&to_book3s(vcpu)->shadow_vcpu, &get_paca()->shadow_vcpu,
+              sizeof(get_paca()->shadow_vcpu));
        to_book3s(vcpu)->slb_shadow_max = get_paca()->kvm_slb_max;
+
+       kvmppc_giveup_ext(vcpu, MSR_FP);
+       kvmppc_giveup_ext(vcpu, MSR_VEC);
+       kvmppc_giveup_ext(vcpu, MSR_VSX);
 }
 
-#if defined(AGGRESSIVE_DEC) || defined(EXIT_DEBUG)
+#if defined(EXIT_DEBUG)
 static u32 kvmppc_get_dec(struct kvm_vcpu *vcpu)
 {
        u64 jd = mftb() - vcpu->arch.dec_jiffies;
@@ -89,6 +94,23 @@ static u32 kvmppc_get_dec(struct kvm_vcpu *vcpu)
 }
 #endif
 
+static void kvmppc_recalc_shadow_msr(struct kvm_vcpu *vcpu)
+{
+       vcpu->arch.shadow_msr = vcpu->arch.msr;
+       /* Guest MSR values */
+       vcpu->arch.shadow_msr &= MSR_FE0 | MSR_FE1 | MSR_SF | MSR_SE |
+                                MSR_BE | MSR_DE;
+       /* Process MSR values */
+       vcpu->arch.shadow_msr |= MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_PR |
+                                MSR_EE;
+       /* External providers the guest reserved */
+       vcpu->arch.shadow_msr |= (vcpu->arch.msr & vcpu->arch.guest_owned_ext);
+       /* 64-bit Process MSR values */
+#ifdef CONFIG_PPC_BOOK3S_64
+       vcpu->arch.shadow_msr |= MSR_ISF | MSR_HV;
+#endif
+}
+
 void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr)
 {
        ulong old_msr = vcpu->arch.msr;
@@ -96,12 +118,10 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr)
 #ifdef EXIT_DEBUG
        printk(KERN_INFO "KVM: Set MSR to 0x%llx\n", msr);
 #endif
+
        msr &= to_book3s(vcpu)->msr_mask;
        vcpu->arch.msr = msr;
-       vcpu->arch.shadow_msr = msr | MSR_USER32;
-       vcpu->arch.shadow_msr &= ( MSR_VEC | MSR_VSX | MSR_FP | MSR_FE0 |
-                                  MSR_USER64 | MSR_SE | MSR_BE | MSR_DE |
-                                  MSR_FE1);
+       kvmppc_recalc_shadow_msr(vcpu);
 
        if (msr & (MSR_WE|MSR_POW)) {
                if (!vcpu->arch.pending_exceptions) {
@@ -125,11 +145,10 @@ void kvmppc_inject_interrupt(struct kvm_vcpu *vcpu, int vec, u64 flags)
        vcpu->arch.mmu.reset_msr(vcpu);
 }
 
-void kvmppc_book3s_queue_irqprio(struct kvm_vcpu *vcpu, unsigned int vec)
+static int kvmppc_book3s_vec2irqprio(unsigned int vec)
 {
        unsigned int prio;
 
-       vcpu->stat.queue_intr++;
        switch (vec) {
        case 0x100: prio = BOOK3S_IRQPRIO_SYSTEM_RESET;         break;
        case 0x200: prio = BOOK3S_IRQPRIO_MACHINE_CHECK;        break;
@@ -149,15 +168,31 @@ void kvmppc_book3s_queue_irqprio(struct kvm_vcpu *vcpu, unsigned int vec)
        default:    prio = BOOK3S_IRQPRIO_MAX;                  break;
        }
 
-       set_bit(prio, &vcpu->arch.pending_exceptions);
+       return prio;
+}
+
+static void kvmppc_book3s_dequeue_irqprio(struct kvm_vcpu *vcpu,
+                                         unsigned int vec)
+{
+       clear_bit(kvmppc_book3s_vec2irqprio(vec),
+                 &vcpu->arch.pending_exceptions);
+}
+
+void kvmppc_book3s_queue_irqprio(struct kvm_vcpu *vcpu, unsigned int vec)
+{
+       vcpu->stat.queue_intr++;
+
+       set_bit(kvmppc_book3s_vec2irqprio(vec),
+               &vcpu->arch.pending_exceptions);
 #ifdef EXIT_DEBUG
        printk(KERN_INFO "Queueing interrupt %x\n", vec);
 #endif
 }
 
 
-void kvmppc_core_queue_program(struct kvm_vcpu *vcpu)
+void kvmppc_core_queue_program(struct kvm_vcpu *vcpu, ulong flags)
 {
+       to_book3s(vcpu)->prog_flags = flags;
        kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_PROGRAM);
 }
 
@@ -171,6 +206,11 @@ int kvmppc_core_pending_dec(struct kvm_vcpu *vcpu)
        return test_bit(BOOK3S_INTERRUPT_DECREMENTER >> 7, &vcpu->arch.pending_exceptions);
 }
 
+void kvmppc_core_dequeue_dec(struct kvm_vcpu *vcpu)
+{
+       kvmppc_book3s_dequeue_irqprio(vcpu, BOOK3S_INTERRUPT_DECREMENTER);
+}
+
 void kvmppc_core_queue_external(struct kvm_vcpu *vcpu,
                                 struct kvm_interrupt *irq)
 {
@@ -181,6 +221,7 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
 {
        int deliver = 1;
        int vec = 0;
+       ulong flags = 0ULL;
 
        switch (priority) {
        case BOOK3S_IRQPRIO_DECREMENTER:
@@ -214,6 +255,7 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
                break;
        case BOOK3S_IRQPRIO_PROGRAM:
                vec = BOOK3S_INTERRUPT_PROGRAM;
+               flags = to_book3s(vcpu)->prog_flags;
                break;
        case BOOK3S_IRQPRIO_VSX:
                vec = BOOK3S_INTERRUPT_VSX;
@@ -244,7 +286,7 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
 #endif
 
        if (deliver)
-               kvmppc_inject_interrupt(vcpu, vec, 0ULL);
+               kvmppc_inject_interrupt(vcpu, vec, flags);
 
        return deliver;
 }
@@ -254,21 +296,15 @@ void kvmppc_core_deliver_interrupts(struct kvm_vcpu *vcpu)
        unsigned long *pending = &vcpu->arch.pending_exceptions;
        unsigned int priority;
 
-       /* XXX be more clever here - no need to mftb() on every entry */
-       /* Issue DEC again if it's still active */
-#ifdef AGGRESSIVE_DEC
-       if (vcpu->arch.msr & MSR_EE)
-               if (kvmppc_get_dec(vcpu) & 0x80000000)
-                       kvmppc_core_queue_dec(vcpu);
-#endif
-
 #ifdef EXIT_DEBUG
        if (vcpu->arch.pending_exceptions)
                printk(KERN_EMERG "KVM: Check pending: %lx\n", vcpu->arch.pending_exceptions);
 #endif
        priority = __ffs(*pending);
        while (priority <= (sizeof(unsigned int) * 8)) {
-               if (kvmppc_book3s_irqprio_deliver(vcpu, priority)) {
+               if (kvmppc_book3s_irqprio_deliver(vcpu, priority) &&
+                   (priority != BOOK3S_IRQPRIO_DECREMENTER)) {
+                       /* DEC interrupts get cleared by mtdec */
                        clear_bit(priority, &vcpu->arch.pending_exceptions);
                        break;
                }
@@ -503,14 +539,14 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                /* Page not found in guest PTE entries */
                vcpu->arch.dear = vcpu->arch.fault_dear;
                to_book3s(vcpu)->dsisr = vcpu->arch.fault_dsisr;
-               vcpu->arch.msr |= (vcpu->arch.shadow_msr & 0x00000000f8000000ULL);
+               vcpu->arch.msr |= (vcpu->arch.shadow_srr1 & 0x00000000f8000000ULL);
                kvmppc_book3s_queue_irqprio(vcpu, vec);
        } else if (page_found == -EPERM) {
                /* Storage protection */
                vcpu->arch.dear = vcpu->arch.fault_dear;
                to_book3s(vcpu)->dsisr = vcpu->arch.fault_dsisr & ~DSISR_NOHPTE;
                to_book3s(vcpu)->dsisr |= DSISR_PROTFAULT;
-               vcpu->arch.msr |= (vcpu->arch.shadow_msr & 0x00000000f8000000ULL);
+               vcpu->arch.msr |= (vcpu->arch.shadow_srr1 & 0x00000000f8000000ULL);
                kvmppc_book3s_queue_irqprio(vcpu, vec);
        } else if (page_found == -EINVAL) {
                /* Page not found in guest SLB */
@@ -532,13 +568,122 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu,
                r = kvmppc_emulate_mmio(run, vcpu);
                if ( r == RESUME_HOST_NV )
                        r = RESUME_HOST;
-               if ( r == RESUME_GUEST_NV )
-                       r = RESUME_GUEST;
        }
 
        return r;
 }
 
+static inline int get_fpr_index(int i)
+{
+#ifdef CONFIG_VSX
+       i *= 2;
+#endif
+       return i;
+}
+
+/* Give up external provider (FPU, Altivec, VSX) */
+static void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr)
+{
+       struct thread_struct *t = &current->thread;
+       u64 *vcpu_fpr = vcpu->arch.fpr;
+       u64 *vcpu_vsx = vcpu->arch.vsr;
+       u64 *thread_fpr = (u64*)t->fpr;
+       int i;
+
+       if (!(vcpu->arch.guest_owned_ext & msr))
+               return;
+
+#ifdef DEBUG_EXT
+       printk(KERN_INFO "Giving up ext 0x%lx\n", msr);
+#endif
+
+       switch (msr) {
+       case MSR_FP:
+               giveup_fpu(current);
+               for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++)
+                       vcpu_fpr[i] = thread_fpr[get_fpr_index(i)];
+
+               vcpu->arch.fpscr = t->fpscr.val;
+               break;
+       case MSR_VEC:
+#ifdef CONFIG_ALTIVEC
+               giveup_altivec(current);
+               memcpy(vcpu->arch.vr, t->vr, sizeof(vcpu->arch.vr));
+               vcpu->arch.vscr = t->vscr;
+#endif
+               break;
+       case MSR_VSX:
+#ifdef CONFIG_VSX
+               __giveup_vsx(current);
+               for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr); i++)
+                       vcpu_vsx[i] = thread_fpr[get_fpr_index(i) + 1];
+#endif
+               break;
+       default:
+               BUG();
+       }
+
+       vcpu->arch.guest_owned_ext &= ~msr;
+       current->thread.regs->msr &= ~msr;
+       kvmppc_recalc_shadow_msr(vcpu);
+}
+
+/* Handle external providers (FPU, Altivec, VSX) */
+static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
+                            ulong msr)
+{
+       struct thread_struct *t = &current->thread;
+       u64 *vcpu_fpr = vcpu->arch.fpr;
+       u64 *vcpu_vsx = vcpu->arch.vsr;
+       u64 *thread_fpr = (u64*)t->fpr;
+       int i;
+
+       if (!(vcpu->arch.msr & msr)) {
+               kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
+               return RESUME_GUEST;
+       }
+
+#ifdef DEBUG_EXT
+       printk(KERN_INFO "Loading up ext 0x%lx\n", msr);
+#endif
+
+       current->thread.regs->msr |= msr;
+
+       switch (msr) {
+       case MSR_FP:
+               for (i = 0; i < ARRAY_SIZE(vcpu->arch.fpr); i++)
+                       thread_fpr[get_fpr_index(i)] = vcpu_fpr[i];
+
+               t->fpscr.val = vcpu->arch.fpscr;
+               t->fpexc_mode = 0;
+               kvmppc_load_up_fpu();
+               break;
+       case MSR_VEC:
+#ifdef CONFIG_ALTIVEC
+               memcpy(t->vr, vcpu->arch.vr, sizeof(vcpu->arch.vr));
+               t->vscr = vcpu->arch.vscr;
+               t->vrsave = -1;
+               kvmppc_load_up_altivec();
+#endif
+               break;
+       case MSR_VSX:
+#ifdef CONFIG_VSX
+               for (i = 0; i < ARRAY_SIZE(vcpu->arch.vsr); i++)
+                       thread_fpr[get_fpr_index(i) + 1] = vcpu_vsx[i];
+               kvmppc_load_up_vsx();
+#endif
+               break;
+       default:
+               BUG();
+       }
+
+       vcpu->arch.guest_owned_ext |= msr;
+
+       kvmppc_recalc_shadow_msr(vcpu);
+
+       return RESUME_GUEST;
+}
+
 int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        unsigned int exit_nr)
 {
@@ -563,7 +708,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case BOOK3S_INTERRUPT_INST_STORAGE:
                vcpu->stat.pf_instruc++;
                /* only care about PTEG not found errors, but leave NX alone */
-               if (vcpu->arch.shadow_msr & 0x40000000) {
+               if (vcpu->arch.shadow_srr1 & 0x40000000) {
                        r = kvmppc_handle_pagefault(run, vcpu, vcpu->arch.pc, exit_nr);
                        vcpu->stat.sp_instruc++;
                } else if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
@@ -575,7 +720,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                         */
                        kvmppc_mmu_pte_flush(vcpu, vcpu->arch.pc, ~0xFFFULL);
                } else {
-                       vcpu->arch.msr |= (vcpu->arch.shadow_msr & 0x58000000);
+                       vcpu->arch.msr |= vcpu->arch.shadow_srr1 & 0x58000000;
                        kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                        kvmppc_mmu_pte_flush(vcpu, vcpu->arch.pc, ~0xFFFULL);
                        r = RESUME_GUEST;
@@ -621,6 +766,9 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case BOOK3S_INTERRUPT_PROGRAM:
        {
                enum emulation_result er;
+               ulong flags;
+
+               flags = vcpu->arch.shadow_srr1 & 0x1f0000ull;
 
                if (vcpu->arch.msr & MSR_PR) {
 #ifdef EXIT_DEBUG
@@ -628,7 +776,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
 #endif
                        if ((vcpu->arch.last_inst & 0xff0007ff) !=
                            (INS_DCBZ & 0xfffffff7)) {
-                               kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
+                               kvmppc_core_queue_program(vcpu, flags);
                                r = RESUME_GUEST;
                                break;
                        }
@@ -638,12 +786,12 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                er = kvmppc_emulate_instruction(run, vcpu);
                switch (er) {
                case EMULATE_DONE:
-                       r = RESUME_GUEST;
+                       r = RESUME_GUEST_NV;
                        break;
                case EMULATE_FAIL:
                        printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n",
                               __func__, vcpu->arch.pc, vcpu->arch.last_inst);
-                       kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
+                       kvmppc_core_queue_program(vcpu, flags);
                        r = RESUME_GUEST;
                        break;
                default:
@@ -653,23 +801,30 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
        }
        case BOOK3S_INTERRUPT_SYSCALL:
 #ifdef EXIT_DEBUG
-               printk(KERN_INFO "Syscall Nr %d\n", (int)vcpu->arch.gpr[0]);
+               printk(KERN_INFO "Syscall Nr %d\n", (int)kvmppc_get_gpr(vcpu, 0));
 #endif
                vcpu->stat.syscall_exits++;
                kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                r = RESUME_GUEST;
                break;
-       case BOOK3S_INTERRUPT_MACHINE_CHECK:
        case BOOK3S_INTERRUPT_FP_UNAVAIL:
-       case BOOK3S_INTERRUPT_TRACE:
+               r = kvmppc_handle_ext(vcpu, exit_nr, MSR_FP);
+               break;
        case BOOK3S_INTERRUPT_ALTIVEC:
+               r = kvmppc_handle_ext(vcpu, exit_nr, MSR_VEC);
+               break;
        case BOOK3S_INTERRUPT_VSX:
+               r = kvmppc_handle_ext(vcpu, exit_nr, MSR_VSX);
+               break;
+       case BOOK3S_INTERRUPT_MACHINE_CHECK:
+       case BOOK3S_INTERRUPT_TRACE:
                kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
                r = RESUME_GUEST;
                break;
        default:
                /* Ugh - bork here! What did we get? */
-               printk(KERN_EMERG "exit_nr=0x%x | pc=0x%lx | msr=0x%lx\n", exit_nr, vcpu->arch.pc, vcpu->arch.shadow_msr);
+               printk(KERN_EMERG "exit_nr=0x%x | pc=0x%lx | msr=0x%lx\n",
+                       exit_nr, vcpu->arch.pc, vcpu->arch.shadow_srr1);
                r = RESUME_HOST;
                BUG();
                break;
@@ -712,10 +867,10 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        int i;
 
        regs->pc = vcpu->arch.pc;
-       regs->cr = vcpu->arch.cr;
+       regs->cr = kvmppc_get_cr(vcpu);
        regs->ctr = vcpu->arch.ctr;
        regs->lr = vcpu->arch.lr;
-       regs->xer = vcpu->arch.xer;
+       regs->xer = kvmppc_get_xer(vcpu);
        regs->msr = vcpu->arch.msr;
        regs->srr0 = vcpu->arch.srr0;
        regs->srr1 = vcpu->arch.srr1;
@@ -729,7 +884,7 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        regs->sprg7 = vcpu->arch.sprg6;
 
        for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
-               regs->gpr[i] = vcpu->arch.gpr[i];
+               regs->gpr[i] = kvmppc_get_gpr(vcpu, i);
 
        return 0;
 }
@@ -739,10 +894,10 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        int i;
 
        vcpu->arch.pc = regs->pc;
-       vcpu->arch.cr = regs->cr;
+       kvmppc_set_cr(vcpu, regs->cr);
        vcpu->arch.ctr = regs->ctr;
        vcpu->arch.lr = regs->lr;
-       vcpu->arch.xer = regs->xer;
+       kvmppc_set_xer(vcpu, regs->xer);
        kvmppc_set_msr(vcpu, regs->msr);
        vcpu->arch.srr0 = regs->srr0;
        vcpu->arch.srr1 = regs->srr1;
@@ -754,8 +909,8 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        vcpu->arch.sprg6 = regs->sprg5;
        vcpu->arch.sprg7 = regs->sprg6;
 
-       for (i = 0; i < ARRAY_SIZE(vcpu->arch.gpr); i++)
-               vcpu->arch.gpr[i] = regs->gpr[i];
+       for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
+               kvmppc_set_gpr(vcpu, i, regs->gpr[i]);
 
        return 0;
 }
@@ -850,7 +1005,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
        int is_dirty = 0;
        int r, n;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
 
        r = kvm_get_dirty_log(kvm, log, &is_dirty);
        if (r)
@@ -858,7 +1013,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
 
        /* If nothing is dirty, don't bother messing with page tables. */
        if (is_dirty) {
-               memslot = &kvm->memslots[log->slot];
+               memslot = &kvm->memslots->memslots[log->slot];
 
                ga = memslot->base_gfn << PAGE_SHIFT;
                ga_end = ga + (memslot->npages << PAGE_SHIFT);
@@ -872,7 +1027,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
 
        r = 0;
 out:
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return r;
 }
 
@@ -910,6 +1065,7 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id)
        vcpu->arch.trampoline_lowmem = kvmppc_trampoline_lowmem;
        vcpu->arch.trampoline_enter = kvmppc_trampoline_enter;
        vcpu->arch.highmem_handler = (ulong)kvmppc_handler_highmem;
+       vcpu->arch.rmcall = *(ulong*)kvmppc_rmcall;
 
        vcpu->arch.shadow_msr = MSR_USER64;
 
@@ -943,6 +1099,10 @@ extern int __kvmppc_vcpu_entry(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
 int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 {
        int ret;
+       struct thread_struct ext_bkp;
+       bool save_vec = current->thread.used_vr;
+       bool save_vsx = current->thread.used_vsr;
+       ulong ext_msr;
 
        /* No need to go into the guest when all we do is going out */
        if (signal_pending(current)) {
@@ -950,6 +1110,35 @@ int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
                return -EINTR;
        }
 
+       /* Save FPU state in stack */
+       if (current->thread.regs->msr & MSR_FP)
+               giveup_fpu(current);
+       memcpy(ext_bkp.fpr, current->thread.fpr, sizeof(current->thread.fpr));
+       ext_bkp.fpscr = current->thread.fpscr;
+       ext_bkp.fpexc_mode = current->thread.fpexc_mode;
+
+#ifdef CONFIG_ALTIVEC
+       /* Save Altivec state in stack */
+       if (save_vec) {
+               if (current->thread.regs->msr & MSR_VEC)
+                       giveup_altivec(current);
+               memcpy(ext_bkp.vr, current->thread.vr, sizeof(ext_bkp.vr));
+               ext_bkp.vscr = current->thread.vscr;
+               ext_bkp.vrsave = current->thread.vrsave;
+       }
+       ext_bkp.used_vr = current->thread.used_vr;
+#endif
+
+#ifdef CONFIG_VSX
+       /* Save VSX state in stack */
+       if (save_vsx && (current->thread.regs->msr & MSR_VSX))
+                       __giveup_vsx(current);
+       ext_bkp.used_vsr = current->thread.used_vsr;
+#endif
+
+       /* Remember the MSR with disabled extensions */
+       ext_msr = current->thread.regs->msr;
+
        /* XXX we get called with irq disabled - change that! */
        local_irq_enable();
 
@@ -957,6 +1146,32 @@ int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 
        local_irq_disable();
 
+       current->thread.regs->msr = ext_msr;
+
+       /* Make sure we save the guest FPU/Altivec/VSX state */
+       kvmppc_giveup_ext(vcpu, MSR_FP);
+       kvmppc_giveup_ext(vcpu, MSR_VEC);
+       kvmppc_giveup_ext(vcpu, MSR_VSX);
+
+       /* Restore FPU state from stack */
+       memcpy(current->thread.fpr, ext_bkp.fpr, sizeof(ext_bkp.fpr));
+       current->thread.fpscr = ext_bkp.fpscr;
+       current->thread.fpexc_mode = ext_bkp.fpexc_mode;
+
+#ifdef CONFIG_ALTIVEC
+       /* Restore Altivec state from stack */
+       if (save_vec && current->thread.used_vr) {
+               memcpy(current->thread.vr, ext_bkp.vr, sizeof(ext_bkp.vr));
+               current->thread.vscr = ext_bkp.vscr;
+               current->thread.vrsave= ext_bkp.vrsave;
+       }
+       current->thread.used_vr = ext_bkp.used_vr;
+#endif
+
+#ifdef CONFIG_VSX
+       current->thread.used_vsr = ext_bkp.used_vsr;
+#endif
+
        return ret;
 }
 
index 1027eac6d474fea3ae3a94efe6aca91cd48a594c..2b0ee7e040c90d7870851ae46d564b1e17451ad7 100644 (file)
@@ -65,11 +65,11 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case 31:
                switch (get_xop(inst)) {
                case OP_31_XOP_MFMSR:
-                       vcpu->arch.gpr[get_rt(inst)] = vcpu->arch.msr;
+                       kvmppc_set_gpr(vcpu, get_rt(inst), vcpu->arch.msr);
                        break;
                case OP_31_XOP_MTMSRD:
                {
-                       ulong rs = vcpu->arch.gpr[get_rs(inst)];
+                       ulong rs = kvmppc_get_gpr(vcpu, get_rs(inst));
                        if (inst & 0x10000) {
                                vcpu->arch.msr &= ~(MSR_RI | MSR_EE);
                                vcpu->arch.msr |= rs & (MSR_RI | MSR_EE);
@@ -78,30 +78,30 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        break;
                }
                case OP_31_XOP_MTMSR:
-                       kvmppc_set_msr(vcpu, vcpu->arch.gpr[get_rs(inst)]);
+                       kvmppc_set_msr(vcpu, kvmppc_get_gpr(vcpu, get_rs(inst)));
                        break;
                case OP_31_XOP_MFSRIN:
                {
                        int srnum;
 
-                       srnum = (vcpu->arch.gpr[get_rb(inst)] >> 28) & 0xf;
+                       srnum = (kvmppc_get_gpr(vcpu, get_rb(inst)) >> 28) & 0xf;
                        if (vcpu->arch.mmu.mfsrin) {
                                u32 sr;
                                sr = vcpu->arch.mmu.mfsrin(vcpu, srnum);
-                               vcpu->arch.gpr[get_rt(inst)] = sr;
+                               kvmppc_set_gpr(vcpu, get_rt(inst), sr);
                        }
                        break;
                }
                case OP_31_XOP_MTSRIN:
                        vcpu->arch.mmu.mtsrin(vcpu,
-                               (vcpu->arch.gpr[get_rb(inst)] >> 28) & 0xf,
-                               vcpu->arch.gpr[get_rs(inst)]);
+                               (kvmppc_get_gpr(vcpu, get_rb(inst)) >> 28) & 0xf,
+                               kvmppc_get_gpr(vcpu, get_rs(inst)));
                        break;
                case OP_31_XOP_TLBIE:
                case OP_31_XOP_TLBIEL:
                {
                        bool large = (inst & 0x00200000) ? true : false;
-                       ulong addr = vcpu->arch.gpr[get_rb(inst)];
+                       ulong addr = kvmppc_get_gpr(vcpu, get_rb(inst));
                        vcpu->arch.mmu.tlbie(vcpu, addr, large);
                        break;
                }
@@ -111,14 +111,16 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        if (!vcpu->arch.mmu.slbmte)
                                return EMULATE_FAIL;
 
-                       vcpu->arch.mmu.slbmte(vcpu, vcpu->arch.gpr[get_rs(inst)],
-                                               vcpu->arch.gpr[get_rb(inst)]);
+                       vcpu->arch.mmu.slbmte(vcpu,
+                                       kvmppc_get_gpr(vcpu, get_rs(inst)),
+                                       kvmppc_get_gpr(vcpu, get_rb(inst)));
                        break;
                case OP_31_XOP_SLBIE:
                        if (!vcpu->arch.mmu.slbie)
                                return EMULATE_FAIL;
 
-                       vcpu->arch.mmu.slbie(vcpu, vcpu->arch.gpr[get_rb(inst)]);
+                       vcpu->arch.mmu.slbie(vcpu,
+                                       kvmppc_get_gpr(vcpu, get_rb(inst)));
                        break;
                case OP_31_XOP_SLBIA:
                        if (!vcpu->arch.mmu.slbia)
@@ -132,9 +134,9 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        } else {
                                ulong t, rb;
 
-                               rb = vcpu->arch.gpr[get_rb(inst)];
+                               rb = kvmppc_get_gpr(vcpu, get_rb(inst));
                                t = vcpu->arch.mmu.slbmfee(vcpu, rb);
-                               vcpu->arch.gpr[get_rt(inst)] = t;
+                               kvmppc_set_gpr(vcpu, get_rt(inst), t);
                        }
                        break;
                case OP_31_XOP_SLBMFEV:
@@ -143,20 +145,20 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        } else {
                                ulong t, rb;
 
-                               rb = vcpu->arch.gpr[get_rb(inst)];
+                               rb = kvmppc_get_gpr(vcpu, get_rb(inst));
                                t = vcpu->arch.mmu.slbmfev(vcpu, rb);
-                               vcpu->arch.gpr[get_rt(inst)] = t;
+                               kvmppc_set_gpr(vcpu, get_rt(inst), t);
                        }
                        break;
                case OP_31_XOP_DCBZ:
                {
-                       ulong rb =  vcpu->arch.gpr[get_rb(inst)];
+                       ulong rb = kvmppc_get_gpr(vcpu, get_rb(inst));
                        ulong ra = 0;
                        ulong addr;
                        u32 zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 
                        if (get_ra(inst))
-                               ra = vcpu->arch.gpr[get_ra(inst)];
+                               ra = kvmppc_get_gpr(vcpu, get_ra(inst));
 
                        addr = (ra + rb) & ~31ULL;
                        if (!(vcpu->arch.msr & MSR_SF))
@@ -233,43 +235,44 @@ static void kvmppc_write_bat(struct kvm_vcpu *vcpu, int sprn, u32 val)
 int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 {
        int emulated = EMULATE_DONE;
+       ulong spr_val = kvmppc_get_gpr(vcpu, rs);
 
        switch (sprn) {
        case SPRN_SDR1:
-               to_book3s(vcpu)->sdr1 = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->sdr1 = spr_val;
                break;
        case SPRN_DSISR:
-               to_book3s(vcpu)->dsisr = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->dsisr = spr_val;
                break;
        case SPRN_DAR:
-               vcpu->arch.dear = vcpu->arch.gpr[rs];
+               vcpu->arch.dear = spr_val;
                break;
        case SPRN_HIOR:
-               to_book3s(vcpu)->hior = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hior = spr_val;
                break;
        case SPRN_IBAT0U ... SPRN_IBAT3L:
        case SPRN_IBAT4U ... SPRN_IBAT7L:
        case SPRN_DBAT0U ... SPRN_DBAT3L:
        case SPRN_DBAT4U ... SPRN_DBAT7L:
-               kvmppc_write_bat(vcpu, sprn, (u32)vcpu->arch.gpr[rs]);
+               kvmppc_write_bat(vcpu, sprn, (u32)spr_val);
                /* BAT writes happen so rarely that we're ok to flush
                 * everything here */
                kvmppc_mmu_pte_flush(vcpu, 0, 0);
                break;
        case SPRN_HID0:
-               to_book3s(vcpu)->hid[0] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[0] = spr_val;
                break;
        case SPRN_HID1:
-               to_book3s(vcpu)->hid[1] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[1] = spr_val;
                break;
        case SPRN_HID2:
-               to_book3s(vcpu)->hid[2] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[2] = spr_val;
                break;
        case SPRN_HID4:
-               to_book3s(vcpu)->hid[4] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[4] = spr_val;
                break;
        case SPRN_HID5:
-               to_book3s(vcpu)->hid[5] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[5] = spr_val;
                /* guest HID5 set can change is_dcbz32 */
                if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
                    (mfmsr() & MSR_HV))
@@ -299,38 +302,38 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
 
        switch (sprn) {
        case SPRN_SDR1:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->sdr1;
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->sdr1);
                break;
        case SPRN_DSISR:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->dsisr;
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->dsisr);
                break;
        case SPRN_DAR:
-               vcpu->arch.gpr[rt] = vcpu->arch.dear;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dear);
                break;
        case SPRN_HIOR:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hior;
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hior);
                break;
        case SPRN_HID0:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[0];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[0]);
                break;
        case SPRN_HID1:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[1];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[1]);
                break;
        case SPRN_HID2:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[2];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[2]);
                break;
        case SPRN_HID4:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[4];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[4]);
                break;
        case SPRN_HID5:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[5];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[5]);
                break;
        case SPRN_THRM1:
        case SPRN_THRM2:
        case SPRN_THRM3:
        case SPRN_CTRLF:
        case SPRN_CTRLT:
-               vcpu->arch.gpr[rt] = 0;
+               kvmppc_set_gpr(vcpu, rt, 0);
                break;
        default:
                printk(KERN_INFO "KVM: invalid SPR read: %d\n", sprn);
index 5b2db38ed86cce3c1d69309308f0a49c63806ee4..1dd5a1ddfd0dfba437043c1aa4cfb3f3ed4aca62 100644 (file)
 
 EXPORT_SYMBOL_GPL(kvmppc_trampoline_enter);
 EXPORT_SYMBOL_GPL(kvmppc_trampoline_lowmem);
+EXPORT_SYMBOL_GPL(kvmppc_rmcall);
+EXPORT_SYMBOL_GPL(kvmppc_load_up_fpu);
+#ifdef CONFIG_ALTIVEC
+EXPORT_SYMBOL_GPL(kvmppc_load_up_altivec);
+#endif
+#ifdef CONFIG_VSX
+EXPORT_SYMBOL_GPL(kvmppc_load_up_vsx);
+#endif
index 7b55d8094c8b372781bea6a2c35c689b0f273cc8..c1584d0cbce82d3ca9f3dd0565d6d5adacbfc3c7 100644 (file)
 #define ULONG_SIZE 8
 #define VCPU_GPR(n)     (VCPU_GPRS + (n * ULONG_SIZE))
 
-.macro mfpaca tmp_reg, src_reg, offset, vcpu_reg
-       ld      \tmp_reg, (PACA_EXMC+\offset)(r13)
-       std     \tmp_reg, VCPU_GPR(\src_reg)(\vcpu_reg)
-.endm
-
 .macro DISABLE_INTERRUPTS
        mfmsr   r0
        rldicl  r0,r0,48,1
        mtmsrd  r0,1
 .endm
 
+#define VCPU_LOAD_NVGPRS(vcpu) \
+       ld      r14, VCPU_GPR(r14)(vcpu); \
+       ld      r15, VCPU_GPR(r15)(vcpu); \
+       ld      r16, VCPU_GPR(r16)(vcpu); \
+       ld      r17, VCPU_GPR(r17)(vcpu); \
+       ld      r18, VCPU_GPR(r18)(vcpu); \
+       ld      r19, VCPU_GPR(r19)(vcpu); \
+       ld      r20, VCPU_GPR(r20)(vcpu); \
+       ld      r21, VCPU_GPR(r21)(vcpu); \
+       ld      r22, VCPU_GPR(r22)(vcpu); \
+       ld      r23, VCPU_GPR(r23)(vcpu); \
+       ld      r24, VCPU_GPR(r24)(vcpu); \
+       ld      r25, VCPU_GPR(r25)(vcpu); \
+       ld      r26, VCPU_GPR(r26)(vcpu); \
+       ld      r27, VCPU_GPR(r27)(vcpu); \
+       ld      r28, VCPU_GPR(r28)(vcpu); \
+       ld      r29, VCPU_GPR(r29)(vcpu); \
+       ld      r30, VCPU_GPR(r30)(vcpu); \
+       ld      r31, VCPU_GPR(r31)(vcpu); \
+
 /*****************************************************************************
  *                                                                           *
  *     Guest entry / exit code that is in kernel module memory (highmem)     *
@@ -67,61 +82,32 @@ kvm_start_entry:
        SAVE_NVGPRS(r1)
 
        /* Save LR */
-       mflr    r14
-       std     r14, _LINK(r1)
-
-/* XXX optimize non-volatile loading away */
-kvm_start_lightweight:
+       std     r0, _LINK(r1)
 
-       DISABLE_INTERRUPTS
+       /* Load non-volatile guest state from the vcpu */
+       VCPU_LOAD_NVGPRS(r4)
 
        /* Save R1/R2 in the PACA */
-       std     r1, PACAR1(r13)
-       std     r2, (PACA_EXMC+EX_SRR0)(r13)
+       std     r1, PACA_KVM_HOST_R1(r13)
+       std     r2, PACA_KVM_HOST_R2(r13)
+
+       /* XXX swap in/out on load? */
        ld      r3, VCPU_HIGHMEM_HANDLER(r4)
-       std     r3, PACASAVEDMSR(r13)
+       std     r3, PACA_KVM_VMHANDLER(r13)
 
-       /* Load non-volatile guest state from the vcpu */
-       ld      r14, VCPU_GPR(r14)(r4)
-       ld      r15, VCPU_GPR(r15)(r4)
-       ld      r16, VCPU_GPR(r16)(r4)
-       ld      r17, VCPU_GPR(r17)(r4)
-       ld      r18, VCPU_GPR(r18)(r4)
-       ld      r19, VCPU_GPR(r19)(r4)
-       ld      r20, VCPU_GPR(r20)(r4)
-       ld      r21, VCPU_GPR(r21)(r4)
-       ld      r22, VCPU_GPR(r22)(r4)
-       ld      r23, VCPU_GPR(r23)(r4)
-       ld      r24, VCPU_GPR(r24)(r4)
-       ld      r25, VCPU_GPR(r25)(r4)
-       ld      r26, VCPU_GPR(r26)(r4)
-       ld      r27, VCPU_GPR(r27)(r4)
-       ld      r28, VCPU_GPR(r28)(r4)
-       ld      r29, VCPU_GPR(r29)(r4)
-       ld      r30, VCPU_GPR(r30)(r4)
-       ld      r31, VCPU_GPR(r31)(r4)
+kvm_start_lightweight:
 
        ld      r9, VCPU_PC(r4)                 /* r9 = vcpu->arch.pc */
        ld      r10, VCPU_SHADOW_MSR(r4)        /* r10 = vcpu->arch.shadow_msr */
 
-       ld      r3, VCPU_TRAMPOLINE_ENTER(r4)
-       mtsrr0  r3
-
-       LOAD_REG_IMMEDIATE(r3, MSR_KERNEL & ~(MSR_IR | MSR_DR))
-       mtsrr1  r3
-
-       /* Load guest state in the respective registers */
-       lwz     r3, VCPU_CR(r4)         /* r3 = vcpu->arch.cr */
-       stw     r3, (PACA_EXMC + EX_CCR)(r13)
-
-       ld      r3, VCPU_CTR(r4)        /* r3 = vcpu->arch.ctr */
-       mtctr   r3                      /* CTR = r3 */
+       /* Load some guest state in the respective registers */
+       ld      r5, VCPU_CTR(r4)        /* r5 = vcpu->arch.ctr */
+                                       /* will be swapped in by rmcall */
 
        ld      r3, VCPU_LR(r4)         /* r3 = vcpu->arch.lr */
        mtlr    r3                      /* LR = r3 */
 
-       ld      r3, VCPU_XER(r4)        /* r3 = vcpu->arch.xer */
-       std     r3, (PACA_EXMC + EX_R3)(r13)
+       DISABLE_INTERRUPTS
 
        /* Some guests may need to have dcbz set to 32 byte length.
         *
@@ -141,36 +127,15 @@ kvm_start_lightweight:
        mtspr   SPRN_HID5,r3
 
 no_dcbz32_on:
-       /*      Load guest GPRs */
-
-       ld      r3, VCPU_GPR(r9)(r4)
-       std     r3, (PACA_EXMC + EX_R9)(r13)
-       ld      r3, VCPU_GPR(r10)(r4)
-       std     r3, (PACA_EXMC + EX_R10)(r13)
-       ld      r3, VCPU_GPR(r11)(r4)
-       std     r3, (PACA_EXMC + EX_R11)(r13)
-       ld      r3, VCPU_GPR(r12)(r4)
-       std     r3, (PACA_EXMC + EX_R12)(r13)
-       ld      r3, VCPU_GPR(r13)(r4)
-       std     r3, (PACA_EXMC + EX_R13)(r13)
-
-       ld      r0, VCPU_GPR(r0)(r4)
-       ld      r1, VCPU_GPR(r1)(r4)
-       ld      r2, VCPU_GPR(r2)(r4)
-       ld      r3, VCPU_GPR(r3)(r4)
-       ld      r5, VCPU_GPR(r5)(r4)
-       ld      r6, VCPU_GPR(r6)(r4)
-       ld      r7, VCPU_GPR(r7)(r4)
-       ld      r8, VCPU_GPR(r8)(r4)
-       ld      r4, VCPU_GPR(r4)(r4)
-
-       /* This sets the Magic value for the trampoline */
-
-       li      r11, 1
-       stb     r11, PACA_KVM_IN_GUEST(r13)
+
+       ld      r6, VCPU_RMCALL(r4)
+       mtctr   r6
+
+       ld      r3, VCPU_TRAMPOLINE_ENTER(r4)
+       LOAD_REG_IMMEDIATE(r4, MSR_KERNEL & ~(MSR_IR | MSR_DR))
 
        /* Jump to SLB patching handlder and into our guest */
-       RFI
+       bctr
 
 /*
  * This is the handler in module memory. It gets jumped at from the
@@ -184,125 +149,70 @@ kvmppc_handler_highmem:
        /*
         * Register usage at this point:
         *
-        * R00   = guest R13
-        * R01   = host R1
-        * R02   = host R2
-        * R10   = guest PC
-        * R11   = guest MSR
-        * R12   = exit handler id
-        * R13   = PACA
-        * PACA.exmc.R9    = guest R1
-        * PACA.exmc.R10   = guest R10
-        * PACA.exmc.R11   = guest R11
-        * PACA.exmc.R12   = guest R12
-        * PACA.exmc.R13   = guest R2
-        * PACA.exmc.DAR   = guest DAR
-        * PACA.exmc.DSISR = guest DSISR
-        * PACA.exmc.LR    = guest instruction
-        * PACA.exmc.CCR   = guest CR
-        * PACA.exmc.SRR0  = guest R0
+        * R0         = guest last inst
+        * R1         = host R1
+        * R2         = host R2
+        * R3         = guest PC
+        * R4         = guest MSR
+        * R5         = guest DAR
+        * R6         = guest DSISR
+        * R13        = PACA
+        * PACA.KVM.* = guest *
         *
         */
 
-       std     r3, (PACA_EXMC+EX_R3)(r13)
+       /* R7 = vcpu */
+       ld      r7, GPR4(r1)
 
-       /* save the exit id in R3 */
-       mr      r3, r12
+       /* Now save the guest state */
 
-       /* R12 = vcpu */
-       ld      r12, GPR4(r1)
+       stw     r0, VCPU_LAST_INST(r7)
 
-       /* Now save the guest state */
+       std     r3, VCPU_PC(r7)
+       std     r4, VCPU_SHADOW_SRR1(r7)
+       std     r5, VCPU_FAULT_DEAR(r7)
+       std     r6, VCPU_FAULT_DSISR(r7)
 
-       std     r0, VCPU_GPR(r13)(r12)
-       std     r4, VCPU_GPR(r4)(r12)
-       std     r5, VCPU_GPR(r5)(r12)
-       std     r6, VCPU_GPR(r6)(r12)
-       std     r7, VCPU_GPR(r7)(r12)
-       std     r8, VCPU_GPR(r8)(r12)
-       std     r9, VCPU_GPR(r9)(r12)
-
-       /* get registers from PACA */
-       mfpaca  r5, r0, EX_SRR0, r12
-       mfpaca  r5, r3, EX_R3, r12
-       mfpaca  r5, r1, EX_R9, r12
-       mfpaca  r5, r10, EX_R10, r12
-       mfpaca  r5, r11, EX_R11, r12
-       mfpaca  r5, r12, EX_R12, r12
-       mfpaca  r5, r2, EX_R13, r12
-
-       lwz     r5, (PACA_EXMC+EX_LR)(r13)
-       stw     r5, VCPU_LAST_INST(r12)
-
-       lwz     r5, (PACA_EXMC+EX_CCR)(r13)
-       stw     r5, VCPU_CR(r12)
-
-       ld      r5, VCPU_HFLAGS(r12)
+       ld      r5, VCPU_HFLAGS(r7)
        rldicl. r5, r5, 0, 63           /* CR = ((r5 & 1) == 0) */
        beq     no_dcbz32_off
 
+       li      r4, 0
        mfspr   r5,SPRN_HID5
-       rldimi  r5,r5,6,56
+       rldimi  r5,r4,6,56
        mtspr   SPRN_HID5,r5
 
 no_dcbz32_off:
 
-       /* XXX maybe skip on lightweight? */
-       std     r14, VCPU_GPR(r14)(r12)
-       std     r15, VCPU_GPR(r15)(r12)
-       std     r16, VCPU_GPR(r16)(r12)
-       std     r17, VCPU_GPR(r17)(r12)
-       std     r18, VCPU_GPR(r18)(r12)
-       std     r19, VCPU_GPR(r19)(r12)
-       std     r20, VCPU_GPR(r20)(r12)
-       std     r21, VCPU_GPR(r21)(r12)
-       std     r22, VCPU_GPR(r22)(r12)
-       std     r23, VCPU_GPR(r23)(r12)
-       std     r24, VCPU_GPR(r24)(r12)
-       std     r25, VCPU_GPR(r25)(r12)
-       std     r26, VCPU_GPR(r26)(r12)
-       std     r27, VCPU_GPR(r27)(r12)
-       std     r28, VCPU_GPR(r28)(r12)
-       std     r29, VCPU_GPR(r29)(r12)
-       std     r30, VCPU_GPR(r30)(r12)
-       std     r31, VCPU_GPR(r31)(r12)
-
-       /* Restore non-volatile host registers (r14 - r31) */
-       REST_NVGPRS(r1)
-
-       /* Save guest PC (R10) */
-       std     r10, VCPU_PC(r12)
-
-       /* Save guest msr (R11) */
-       std     r11, VCPU_SHADOW_MSR(r12)
-
-       /* Save guest CTR (in R12) */
+       std     r14, VCPU_GPR(r14)(r7)
+       std     r15, VCPU_GPR(r15)(r7)
+       std     r16, VCPU_GPR(r16)(r7)
+       std     r17, VCPU_GPR(r17)(r7)
+       std     r18, VCPU_GPR(r18)(r7)
+       std     r19, VCPU_GPR(r19)(r7)
+       std     r20, VCPU_GPR(r20)(r7)
+       std     r21, VCPU_GPR(r21)(r7)
+       std     r22, VCPU_GPR(r22)(r7)
+       std     r23, VCPU_GPR(r23)(r7)
+       std     r24, VCPU_GPR(r24)(r7)
+       std     r25, VCPU_GPR(r25)(r7)
+       std     r26, VCPU_GPR(r26)(r7)
+       std     r27, VCPU_GPR(r27)(r7)
+       std     r28, VCPU_GPR(r28)(r7)
+       std     r29, VCPU_GPR(r29)(r7)
+       std     r30, VCPU_GPR(r30)(r7)
+       std     r31, VCPU_GPR(r31)(r7)
+
+       /* Save guest CTR */
        mfctr   r5
-       std     r5, VCPU_CTR(r12)
+       std     r5, VCPU_CTR(r7)
 
        /* Save guest LR */
        mflr    r5
-       std     r5, VCPU_LR(r12)
-
-       /* Save guest XER */
-       mfxer   r5
-       std     r5, VCPU_XER(r12)
-
-       /* Save guest DAR */
-       ld      r5, (PACA_EXMC+EX_DAR)(r13)
-       std     r5, VCPU_FAULT_DEAR(r12)
-
-       /* Save guest DSISR */
-       lwz     r5, (PACA_EXMC+EX_DSISR)(r13)
-       std     r5, VCPU_FAULT_DSISR(r12)
+       std     r5, VCPU_LR(r7)
 
        /* Restore host msr -> SRR1 */
-       ld      r7, VCPU_HOST_MSR(r12)
-       mtsrr1  r7
-
-       /* Restore host IP -> SRR0 */
-       ld      r6, VCPU_HOST_RETIP(r12)
-       mtsrr0  r6
+       ld      r6, VCPU_HOST_MSR(r7)
 
        /*
         * For some interrupts, we need to call the real Linux
@@ -314,13 +224,14 @@ no_dcbz32_off:
         * r3 = address of interrupt handler (exit reason)
         */
 
-       cmpwi   r3, BOOK3S_INTERRUPT_EXTERNAL
+       cmpwi   r12, BOOK3S_INTERRUPT_EXTERNAL
        beq     call_linux_handler
-       cmpwi   r3, BOOK3S_INTERRUPT_DECREMENTER
+       cmpwi   r12, BOOK3S_INTERRUPT_DECREMENTER
        beq     call_linux_handler
 
-       /* Back to Interruptable Mode! (goto kvm_return_point) */
-       RFI
+       /* Back to EE=1 */
+       mtmsr   r6
+       b       kvm_return_point
 
 call_linux_handler:
 
@@ -333,16 +244,22 @@ call_linux_handler:
         * interrupt handler!
         *
         * R3 still contains the exit code,
-        * R6 VCPU_HOST_RETIP and
-        * R7 VCPU_HOST_MSR
+        * R5 VCPU_HOST_RETIP and
+        * R6 VCPU_HOST_MSR
         */
 
-       mtlr    r3
+       /* Restore host IP -> SRR0 */
+       ld      r5, VCPU_HOST_RETIP(r7)
+
+       /* XXX Better move to a safe function?
+        *     What if we get an HTAB flush in between mtsrr0 and mtsrr1? */
 
-       ld      r5, VCPU_TRAMPOLINE_LOWMEM(r12)
-       mtsrr0  r5
-       LOAD_REG_IMMEDIATE(r5, MSR_KERNEL & ~(MSR_IR | MSR_DR))
-       mtsrr1  r5
+       mtlr    r12
+
+       ld      r4, VCPU_TRAMPOLINE_LOWMEM(r7)
+       mtsrr0  r4
+       LOAD_REG_IMMEDIATE(r3, MSR_KERNEL & ~(MSR_IR | MSR_DR))
+       mtsrr1  r3
 
        RFI
 
@@ -351,42 +268,51 @@ kvm_return_point:
 
        /* Jump back to lightweight entry if we're supposed to */
        /* go back into the guest */
-       mr      r5, r3
+
+       /* Pass the exit number as 3rd argument to kvmppc_handle_exit */
+       mr      r5, r12
+
        /* Restore r3 (kvm_run) and r4 (vcpu) */
        REST_2GPRS(3, r1)
        bl      KVMPPC_HANDLE_EXIT
 
-#if 0 /* XXX get lightweight exits back */
+       /* If RESUME_GUEST, get back in the loop */
        cmpwi   r3, RESUME_GUEST
-       bne     kvm_exit_heavyweight
+       beq     kvm_loop_lightweight
 
-       /* put VCPU and KVM_RUN back into place and roll again! */
-       REST_2GPRS(3, r1)
-       b       kvm_start_lightweight
+       cmpwi   r3, RESUME_GUEST_NV
+       beq     kvm_loop_heavyweight
 
-kvm_exit_heavyweight:
-       /* Restore non-volatile host registers */
-       ld      r14, _LINK(r1)
-       mtlr    r14
-       REST_NVGPRS(r1)
+kvm_exit_loop:
 
-       addi    r1, r1, SWITCH_FRAME_SIZE
-#else
        ld      r4, _LINK(r1)
        mtlr    r4
 
-       cmpwi   r3, RESUME_GUEST
-       bne     kvm_exit_heavyweight
+       /* Restore non-volatile host registers (r14 - r31) */
+       REST_NVGPRS(r1)
+
+       addi    r1, r1, SWITCH_FRAME_SIZE
+       blr
+
+kvm_loop_heavyweight:
+
+       ld      r4, _LINK(r1)
+       std     r4, (16 + SWITCH_FRAME_SIZE)(r1)
 
+       /* Load vcpu and cpu_run */
        REST_2GPRS(3, r1)
 
-       addi    r1, r1, SWITCH_FRAME_SIZE
+       /* Load non-volatile guest state from the vcpu */
+       VCPU_LOAD_NVGPRS(r4)
 
-       b       kvm_start_entry
+       /* Jump back into the beginning of this function */
+       b       kvm_start_lightweight
 
-kvm_exit_heavyweight:
+kvm_loop_lightweight:
 
-       addi    r1, r1, SWITCH_FRAME_SIZE
-#endif
+       /* We'll need the vcpu pointer */
+       REST_GPR(4, r1)
+
+       /* Jump back into the beginning of this function */
+       b       kvm_start_lightweight
 
-       blr
index e4beeb371a732fecdf3fc5fe1291ae01aa40b2a6..512dcff77554aa6d76bd3b432b2b1aa0cdb7cd93 100644 (file)
@@ -54,7 +54,7 @@ static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe(
                if (!vcpu_book3s->slb[i].valid)
                        continue;
 
-               if (vcpu_book3s->slb[i].large)
+               if (vcpu_book3s->slb[i].tb)
                        cmp_esid = esid_1t;
 
                if (vcpu_book3s->slb[i].esid == cmp_esid)
@@ -65,9 +65,10 @@ static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe(
                eaddr, esid, esid_1t);
        for (i = 0; i < vcpu_book3s->slb_nr; i++) {
            if (vcpu_book3s->slb[i].vsid)
-               dprintk("  %d: %c%c %llx %llx\n", i,
+               dprintk("  %d: %c%c%c %llx %llx\n", i,
                        vcpu_book3s->slb[i].valid ? 'v' : ' ',
                        vcpu_book3s->slb[i].large ? 'l' : ' ',
+                       vcpu_book3s->slb[i].tb    ? 't' : ' ',
                        vcpu_book3s->slb[i].esid,
                        vcpu_book3s->slb[i].vsid);
        }
@@ -84,7 +85,7 @@ static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr,
        if (!slb)
                return 0;
 
-       if (slb->large)
+       if (slb->tb)
                return (((u64)eaddr >> 12) & 0xfffffff) |
                       (((u64)slb->vsid) << 28);
 
@@ -309,7 +310,8 @@ static void kvmppc_mmu_book3s_64_slbmte(struct kvm_vcpu *vcpu, u64 rs, u64 rb)
        slbe = &vcpu_book3s->slb[slb_nr];
 
        slbe->large = (rs & SLB_VSID_L) ? 1 : 0;
-       slbe->esid  = slbe->large ? esid_1t : esid;
+       slbe->tb    = (rs & SLB_VSID_B_1T) ? 1 : 0;
+       slbe->esid  = slbe->tb ? esid_1t : esid;
        slbe->vsid  = rs >> 12;
        slbe->valid = (rb & SLB_ESID_V) ? 1 : 0;
        slbe->Ks    = (rs & SLB_VSID_KS) ? 1 : 0;
index fb7dd2e9ac8863fdfc48be3088cd73dcb6337df4..c83c60ad96c58a791b01913e481a2316a81882a4 100644 (file)
@@ -45,36 +45,25 @@ kvmppc_trampoline_\intno:
         * To distinguish, we check a magic byte in the PACA
         */
        mfspr   r13, SPRN_SPRG_PACA             /* r13 = PACA */
-       std     r12, (PACA_EXMC + EX_R12)(r13)
+       std     r12, PACA_KVM_SCRATCH0(r13)
        mfcr    r12
-       stw     r12, (PACA_EXMC + EX_CCR)(r13)
+       stw     r12, PACA_KVM_SCRATCH1(r13)
        lbz     r12, PACA_KVM_IN_GUEST(r13)
-       cmpwi   r12, 0
+       cmpwi   r12, KVM_GUEST_MODE_NONE
        bne     ..kvmppc_handler_hasmagic_\intno
        /* No KVM guest? Then jump back to the Linux handler! */
-       lwz     r12, (PACA_EXMC + EX_CCR)(r13)
+       lwz     r12, PACA_KVM_SCRATCH1(r13)
        mtcr    r12
-       ld      r12, (PACA_EXMC + EX_R12)(r13)
+       ld      r12, PACA_KVM_SCRATCH0(r13)
        mfspr   r13, SPRN_SPRG_SCRATCH0         /* r13 = original r13 */
        b       kvmppc_resume_\intno            /* Get back original handler */
 
        /* Now we know we're handling a KVM guest */
 ..kvmppc_handler_hasmagic_\intno:
-       /* Unset guest state */
-       li      r12, 0
-       stb     r12, PACA_KVM_IN_GUEST(r13)
 
-       std     r1, (PACA_EXMC+EX_R9)(r13)
-       std     r10, (PACA_EXMC+EX_R10)(r13)
-       std     r11, (PACA_EXMC+EX_R11)(r13)
-       std     r2, (PACA_EXMC+EX_R13)(r13)
-
-       mfsrr0  r10
-       mfsrr1  r11
-
-       /* Restore R1/R2 so we can handle faults */
-       ld      r1, PACAR1(r13)
-       ld      r2, (PACA_EXMC+EX_SRR0)(r13)
+       /* Should we just skip the faulting instruction? */
+       cmpwi   r12, KVM_GUEST_MODE_SKIP
+       beq     kvmppc_handler_skip_ins
 
        /* Let's store which interrupt we're handling */
        li      r12, \intno
@@ -101,24 +90,108 @@ INTERRUPT_TRAMPOLINE      BOOK3S_INTERRUPT_PERFMON
 INTERRUPT_TRAMPOLINE   BOOK3S_INTERRUPT_ALTIVEC
 INTERRUPT_TRAMPOLINE   BOOK3S_INTERRUPT_VSX
 
+/*
+ * Bring us back to the faulting code, but skip the
+ * faulting instruction.
+ *
+ * This is a generic exit path from the interrupt
+ * trampolines above.
+ *
+ * Input Registers:
+ *
+ * R12               = free
+ * R13               = PACA
+ * PACA.KVM.SCRATCH0 = guest R12
+ * PACA.KVM.SCRATCH1 = guest CR
+ * SPRG_SCRATCH0     = guest R13
+ *
+ */
+kvmppc_handler_skip_ins:
+
+       /* Patch the IP to the next instruction */
+       mfsrr0  r12
+       addi    r12, r12, 4
+       mtsrr0  r12
+
+       /* Clean up all state */
+       lwz     r12, PACA_KVM_SCRATCH1(r13)
+       mtcr    r12
+       ld      r12, PACA_KVM_SCRATCH0(r13)
+       mfspr   r13, SPRN_SPRG_SCRATCH0
+
+       /* And get back into the code */
+       RFI
+
 /*
  * This trampoline brings us back to a real mode handler
  *
  * Input Registers:
  *
- * R6 = SRR0
- * R7 = SRR1
+ * R5 = SRR0
+ * R6 = SRR1
  * LR = real-mode IP
  *
  */
 .global kvmppc_handler_lowmem_trampoline
 kvmppc_handler_lowmem_trampoline:
 
-       mtsrr0  r6
-       mtsrr1  r7
+       mtsrr0  r5
+       mtsrr1  r6
        blr
 kvmppc_handler_lowmem_trampoline_end:
 
+/*
+ * Call a function in real mode
+ *
+ * Input Registers:
+ *
+ * R3 = function
+ * R4 = MSR
+ * R5 = CTR
+ *
+ */
+_GLOBAL(kvmppc_rmcall)
+       mtmsr   r4              /* Disable relocation, so mtsrr
+                                  doesn't get interrupted */
+       mtctr   r5
+       mtsrr0  r3
+       mtsrr1  r4
+       RFI
+
+/*
+ * Activate current's external feature (FPU/Altivec/VSX)
+ */
+#define define_load_up(what)                           \
+                                                       \
+_GLOBAL(kvmppc_load_up_ ## what);                      \
+       subi    r1, r1, INT_FRAME_SIZE;                 \
+       mflr    r3;                                     \
+       std     r3, _LINK(r1);                          \
+       mfmsr   r4;                                     \
+       std     r31, GPR3(r1);                          \
+       mr      r31, r4;                                \
+       li      r5, MSR_DR;                             \
+       oris    r5, r5, MSR_EE@h;                       \
+       andc    r4, r4, r5;                             \
+       mtmsr   r4;                                     \
+                                                       \
+       bl      .load_up_ ## what;                      \
+                                                       \
+       mtmsr   r31;                                    \
+       ld      r3, _LINK(r1);                          \
+       ld      r31, GPR3(r1);                          \
+       addi    r1, r1, INT_FRAME_SIZE;                 \
+       mtlr    r3;                                     \
+       blr
+
+define_load_up(fpu)
+#ifdef CONFIG_ALTIVEC
+define_load_up(altivec)
+#endif
+#ifdef CONFIG_VSX
+define_load_up(vsx)
+#endif
+
 .global kvmppc_trampoline_lowmem
 kvmppc_trampoline_lowmem:
        .long kvmppc_handler_lowmem_trampoline - _stext
index ecd237a03fd0be4fb6ec8f853a520ca5f7e0016e..35b762722187ca884bd950bcbb894c4dfae09b32 100644 (file)
@@ -31,7 +31,7 @@
 #define REBOLT_SLB_ENTRY(num) \
        ld      r10, SHADOW_SLB_ESID(num)(r11); \
        cmpdi   r10, 0; \
-       beq     slb_exit_skip_1; \
+       beq     slb_exit_skip_ ## num; \
        oris    r10, r10, SLB_ESID_V@h; \
        ld      r9, SHADOW_SLB_VSID(num)(r11); \
        slbmte  r9, r10; \
@@ -51,23 +51,21 @@ kvmppc_handler_trampoline_enter:
         *
         * MSR = ~IR|DR
         * R13 = PACA
+        * R1 = host R1
+        * R2 = host R2
         * R9 = guest IP
         * R10 = guest MSR
-        * R11 = free
-        * R12 = free
-        * PACA[PACA_EXMC + EX_R9] = guest R9
-        * PACA[PACA_EXMC + EX_R10] = guest R10
-        * PACA[PACA_EXMC + EX_R11] = guest R11
-        * PACA[PACA_EXMC + EX_R12] = guest R12
-        * PACA[PACA_EXMC + EX_R13] = guest R13
-        * PACA[PACA_EXMC + EX_CCR] = guest CR
-        * PACA[PACA_EXMC + EX_R3] = guest XER
+        * all other GPRS = free
+        * PACA[KVM_CR] = guest CR
+        * PACA[KVM_XER] = guest XER
         */
 
        mtsrr0  r9
        mtsrr1  r10
 
-       mtspr   SPRN_SPRG_SCRATCH0, r0
+       /* Activate guest mode, so faults get handled by KVM */
+       li      r11, KVM_GUEST_MODE_GUEST
+       stb     r11, PACA_KVM_IN_GUEST(r13)
 
        /* Remove LPAR shadow entries */
 
@@ -131,20 +129,27 @@ slb_do_enter:
 
        /* Enter guest */
 
-       mfspr   r0, SPRN_SPRG_SCRATCH0
-
-       ld      r9, (PACA_EXMC+EX_R9)(r13)
-       ld      r10, (PACA_EXMC+EX_R10)(r13)
-       ld      r12, (PACA_EXMC+EX_R12)(r13)
-
-       lwz     r11, (PACA_EXMC+EX_CCR)(r13)
+       ld      r0, (PACA_KVM_R0)(r13)
+       ld      r1, (PACA_KVM_R1)(r13)
+       ld      r2, (PACA_KVM_R2)(r13)
+       ld      r3, (PACA_KVM_R3)(r13)
+       ld      r4, (PACA_KVM_R4)(r13)
+       ld      r5, (PACA_KVM_R5)(r13)
+       ld      r6, (PACA_KVM_R6)(r13)
+       ld      r7, (PACA_KVM_R7)(r13)
+       ld      r8, (PACA_KVM_R8)(r13)
+       ld      r9, (PACA_KVM_R9)(r13)
+       ld      r10, (PACA_KVM_R10)(r13)
+       ld      r12, (PACA_KVM_R12)(r13)
+
+       lwz     r11, (PACA_KVM_CR)(r13)
        mtcr    r11
 
-       ld      r11, (PACA_EXMC+EX_R3)(r13)
+       ld      r11, (PACA_KVM_XER)(r13)
        mtxer   r11
 
-       ld      r11, (PACA_EXMC+EX_R11)(r13)
-       ld      r13, (PACA_EXMC+EX_R13)(r13)
+       ld      r11, (PACA_KVM_R11)(r13)
+       ld      r13, (PACA_KVM_R13)(r13)
 
        RFI
 kvmppc_handler_trampoline_enter_end:
@@ -162,28 +167,54 @@ kvmppc_handler_trampoline_exit:
 
        /* Register usage at this point:
         *
-        * SPRG_SCRATCH0 = guest R13
-        * R01           = host R1
-        * R02           = host R2
-        * R10           = guest PC
-        * R11           = guest MSR
-        * R12           = exit handler id
-        * R13           = PACA
-        * PACA.exmc.CCR  = guest CR
-        * PACA.exmc.R9  = guest R1
-        * PACA.exmc.R10 = guest R10
-        * PACA.exmc.R11 = guest R11
-        * PACA.exmc.R12 = guest R12
-        * PACA.exmc.R13 = guest R2
+        * SPRG_SCRATCH0     = guest R13
+        * R12               = exit handler id
+        * R13               = PACA
+        * PACA.KVM.SCRATCH0 = guest R12
+        * PACA.KVM.SCRATCH1 = guest CR
         *
         */
 
        /* Save registers */
 
-       std     r0, (PACA_EXMC+EX_SRR0)(r13)
-       std     r9, (PACA_EXMC+EX_R3)(r13)
-       std     r10, (PACA_EXMC+EX_LR)(r13)
-       std     r11, (PACA_EXMC+EX_DAR)(r13)
+       std     r0, PACA_KVM_R0(r13)
+       std     r1, PACA_KVM_R1(r13)
+       std     r2, PACA_KVM_R2(r13)
+       std     r3, PACA_KVM_R3(r13)
+       std     r4, PACA_KVM_R4(r13)
+       std     r5, PACA_KVM_R5(r13)
+       std     r6, PACA_KVM_R6(r13)
+       std     r7, PACA_KVM_R7(r13)
+       std     r8, PACA_KVM_R8(r13)
+       std     r9, PACA_KVM_R9(r13)
+       std     r10, PACA_KVM_R10(r13)
+       std     r11, PACA_KVM_R11(r13)
+
+       /* Restore R1/R2 so we can handle faults */
+       ld      r1, PACA_KVM_HOST_R1(r13)
+       ld      r2, PACA_KVM_HOST_R2(r13)
+
+       /* Save guest PC and MSR in GPRs */
+       mfsrr0  r3
+       mfsrr1  r4
+
+       /* Get scratch'ed off registers */
+       mfspr   r9, SPRN_SPRG_SCRATCH0
+       std     r9, PACA_KVM_R13(r13)
+
+       ld      r8, PACA_KVM_SCRATCH0(r13)
+       std     r8, PACA_KVM_R12(r13)
+
+       lwz     r7, PACA_KVM_SCRATCH1(r13)
+       stw     r7, PACA_KVM_CR(r13)
+
+       /* Save more register state  */
+
+       mfxer   r6
+       stw     r6, PACA_KVM_XER(r13)
+
+       mfdar   r5
+       mfdsisr r6
 
        /*
         * In order for us to easily get the last instruction,
@@ -202,17 +233,28 @@ kvmppc_handler_trampoline_exit:
 
 ld_last_inst:
        /* Save off the guest instruction we're at */
+
+       /* Set guest mode to 'jump over instruction' so if lwz faults
+        * we'll just continue at the next IP. */
+       li      r9, KVM_GUEST_MODE_SKIP
+       stb     r9, PACA_KVM_IN_GUEST(r13)
+
        /*    1) enable paging for data */
        mfmsr   r9
        ori     r11, r9, MSR_DR                 /* Enable paging for data */
        mtmsr   r11
        /*    2) fetch the instruction */
-       lwz     r0, 0(r10)
+       li      r0, KVM_INST_FETCH_FAILED       /* In case lwz faults */
+       lwz     r0, 0(r3)
        /*    3) disable paging again */
        mtmsr   r9
 
 no_ld_last_inst:
 
+       /* Unset guest mode */
+       li      r9, KVM_GUEST_MODE_NONE
+       stb     r9, PACA_KVM_IN_GUEST(r13)
+
        /* Restore bolted entries from the shadow and fix it along the way */
 
        /* We don't store anything in entry 0, so we don't need to take care of it */
@@ -233,29 +275,27 @@ no_ld_last_inst:
 
 slb_do_exit:
 
-       /* Restore registers */
-
-       ld      r11, (PACA_EXMC+EX_DAR)(r13)
-       ld      r10, (PACA_EXMC+EX_LR)(r13)
-       ld      r9, (PACA_EXMC+EX_R3)(r13)
-
-       /* Save last inst */
-       stw     r0, (PACA_EXMC+EX_LR)(r13)
-
-       /* Save DAR and DSISR before going to paged mode */
-       mfdar   r0
-       std     r0, (PACA_EXMC+EX_DAR)(r13)
-       mfdsisr r0
-       stw     r0, (PACA_EXMC+EX_DSISR)(r13)
+       /* Register usage at this point:
+        *
+        * R0         = guest last inst
+        * R1         = host R1
+        * R2         = host R2
+        * R3         = guest PC
+        * R4         = guest MSR
+        * R5         = guest DAR
+        * R6         = guest DSISR
+        * R12        = exit handler id
+        * R13        = PACA
+        * PACA.KVM.* = guest *
+        *
+        */
 
        /* RFI into the highmem handler */
-       mfmsr   r0
-       ori     r0, r0, MSR_IR|MSR_DR|MSR_RI    /* Enable paging */
-       mtsrr1  r0
-       ld      r0, PACASAVEDMSR(r13)           /* Highmem handler address */
-       mtsrr0  r0
-
-       mfspr   r0, SPRN_SPRG_SCRATCH0
+       mfmsr   r7
+       ori     r7, r7, MSR_IR|MSR_DR|MSR_RI    /* Enable paging */
+       mtsrr1  r7
+       ld      r8, PACA_KVM_VMHANDLER(r13)     /* Highmem handler address */
+       mtsrr0  r8
 
        RFI
 kvmppc_handler_trampoline_exit_end:
index 06f5a9ecc42c9fc950666b44a177b7eadeb668ed..4d686cc6b260a797bbc7dffaa1467b574901714f 100644 (file)
@@ -69,10 +69,10 @@ void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu)
 
        for (i = 0; i < 32; i += 4) {
                printk("gpr%02d: %08lx %08lx %08lx %08lx\n", i,
-                      vcpu->arch.gpr[i],
-                      vcpu->arch.gpr[i+1],
-                      vcpu->arch.gpr[i+2],
-                      vcpu->arch.gpr[i+3]);
+                      kvmppc_get_gpr(vcpu, i),
+                      kvmppc_get_gpr(vcpu, i+1),
+                      kvmppc_get_gpr(vcpu, i+2),
+                      kvmppc_get_gpr(vcpu, i+3));
        }
 }
 
@@ -82,8 +82,32 @@ static void kvmppc_booke_queue_irqprio(struct kvm_vcpu *vcpu,
        set_bit(priority, &vcpu->arch.pending_exceptions);
 }
 
-void kvmppc_core_queue_program(struct kvm_vcpu *vcpu)
+static void kvmppc_core_queue_dtlb_miss(struct kvm_vcpu *vcpu,
+                                        ulong dear_flags, ulong esr_flags)
 {
+       vcpu->arch.queued_dear = dear_flags;
+       vcpu->arch.queued_esr = esr_flags;
+       kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
+}
+
+static void kvmppc_core_queue_data_storage(struct kvm_vcpu *vcpu,
+                                           ulong dear_flags, ulong esr_flags)
+{
+       vcpu->arch.queued_dear = dear_flags;
+       vcpu->arch.queued_esr = esr_flags;
+       kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
+}
+
+static void kvmppc_core_queue_inst_storage(struct kvm_vcpu *vcpu,
+                                           ulong esr_flags)
+{
+       vcpu->arch.queued_esr = esr_flags;
+       kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
+}
+
+void kvmppc_core_queue_program(struct kvm_vcpu *vcpu, ulong esr_flags)
+{
+       vcpu->arch.queued_esr = esr_flags;
        kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
 }
 
@@ -97,6 +121,11 @@ int kvmppc_core_pending_dec(struct kvm_vcpu *vcpu)
        return test_bit(BOOKE_IRQPRIO_DECREMENTER, &vcpu->arch.pending_exceptions);
 }
 
+void kvmppc_core_dequeue_dec(struct kvm_vcpu *vcpu)
+{
+       clear_bit(BOOKE_IRQPRIO_DECREMENTER, &vcpu->arch.pending_exceptions);
+}
+
 void kvmppc_core_queue_external(struct kvm_vcpu *vcpu,
                                 struct kvm_interrupt *irq)
 {
@@ -109,14 +138,19 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
 {
        int allowed = 0;
        ulong msr_mask;
+       bool update_esr = false, update_dear = false;
 
        switch (priority) {
-       case BOOKE_IRQPRIO_PROGRAM:
        case BOOKE_IRQPRIO_DTLB_MISS:
-       case BOOKE_IRQPRIO_ITLB_MISS:
-       case BOOKE_IRQPRIO_SYSCALL:
        case BOOKE_IRQPRIO_DATA_STORAGE:
+               update_dear = true;
+               /* fall through */
        case BOOKE_IRQPRIO_INST_STORAGE:
+       case BOOKE_IRQPRIO_PROGRAM:
+               update_esr = true;
+               /* fall through */
+       case BOOKE_IRQPRIO_ITLB_MISS:
+       case BOOKE_IRQPRIO_SYSCALL:
        case BOOKE_IRQPRIO_FP_UNAVAIL:
        case BOOKE_IRQPRIO_SPE_UNAVAIL:
        case BOOKE_IRQPRIO_SPE_FP_DATA:
@@ -151,6 +185,10 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
                vcpu->arch.srr0 = vcpu->arch.pc;
                vcpu->arch.srr1 = vcpu->arch.msr;
                vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority];
+               if (update_esr == true)
+                       vcpu->arch.esr = vcpu->arch.queued_esr;
+               if (update_dear == true)
+                       vcpu->arch.dear = vcpu->arch.queued_dear;
                kvmppc_set_msr(vcpu, vcpu->arch.msr & msr_mask);
 
                clear_bit(priority, &vcpu->arch.pending_exceptions);
@@ -223,8 +261,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                if (vcpu->arch.msr & MSR_PR) {
                        /* Program traps generated by user-level software must be handled
                         * by the guest kernel. */
-                       vcpu->arch.esr = vcpu->arch.fault_esr;
-                       kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_PROGRAM);
+                       kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr);
                        r = RESUME_GUEST;
                        kvmppc_account_exit(vcpu, USR_PR_INST);
                        break;
@@ -280,16 +317,14 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                break;
 
        case BOOKE_INTERRUPT_DATA_STORAGE:
-               vcpu->arch.dear = vcpu->arch.fault_dear;
-               vcpu->arch.esr = vcpu->arch.fault_esr;
-               kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DATA_STORAGE);
+               kvmppc_core_queue_data_storage(vcpu, vcpu->arch.fault_dear,
+                                              vcpu->arch.fault_esr);
                kvmppc_account_exit(vcpu, DSI_EXITS);
                r = RESUME_GUEST;
                break;
 
        case BOOKE_INTERRUPT_INST_STORAGE:
-               vcpu->arch.esr = vcpu->arch.fault_esr;
-               kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE);
+               kvmppc_core_queue_inst_storage(vcpu, vcpu->arch.fault_esr);
                kvmppc_account_exit(vcpu, ISI_EXITS);
                r = RESUME_GUEST;
                break;
@@ -310,9 +345,9 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                gtlb_index = kvmppc_mmu_dtlb_index(vcpu, eaddr);
                if (gtlb_index < 0) {
                        /* The guest didn't have a mapping for it. */
-                       kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
-                       vcpu->arch.dear = vcpu->arch.fault_dear;
-                       vcpu->arch.esr = vcpu->arch.fault_esr;
+                       kvmppc_core_queue_dtlb_miss(vcpu,
+                                                   vcpu->arch.fault_dear,
+                                                   vcpu->arch.fault_esr);
                        kvmppc_mmu_dtlb_miss(vcpu);
                        kvmppc_account_exit(vcpu, DTLB_REAL_MISS_EXITS);
                        r = RESUME_GUEST;
@@ -426,7 +461,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 {
        vcpu->arch.pc = 0;
        vcpu->arch.msr = 0;
-       vcpu->arch.gpr[1] = (16<<20) - 8; /* -8 for the callee-save LR slot */
+       kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */
 
        vcpu->arch.shadow_pid = 1;
 
@@ -444,10 +479,10 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        int i;
 
        regs->pc = vcpu->arch.pc;
-       regs->cr = vcpu->arch.cr;
+       regs->cr = kvmppc_get_cr(vcpu);
        regs->ctr = vcpu->arch.ctr;
        regs->lr = vcpu->arch.lr;
-       regs->xer = vcpu->arch.xer;
+       regs->xer = kvmppc_get_xer(vcpu);
        regs->msr = vcpu->arch.msr;
        regs->srr0 = vcpu->arch.srr0;
        regs->srr1 = vcpu->arch.srr1;
@@ -461,7 +496,7 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        regs->sprg7 = vcpu->arch.sprg6;
 
        for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
-               regs->gpr[i] = vcpu->arch.gpr[i];
+               regs->gpr[i] = kvmppc_get_gpr(vcpu, i);
 
        return 0;
 }
@@ -471,10 +506,10 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        int i;
 
        vcpu->arch.pc = regs->pc;
-       vcpu->arch.cr = regs->cr;
+       kvmppc_set_cr(vcpu, regs->cr);
        vcpu->arch.ctr = regs->ctr;
        vcpu->arch.lr = regs->lr;
-       vcpu->arch.xer = regs->xer;
+       kvmppc_set_xer(vcpu, regs->xer);
        kvmppc_set_msr(vcpu, regs->msr);
        vcpu->arch.srr0 = regs->srr0;
        vcpu->arch.srr1 = regs->srr1;
@@ -486,8 +521,8 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        vcpu->arch.sprg6 = regs->sprg5;
        vcpu->arch.sprg7 = regs->sprg6;
 
-       for (i = 0; i < ARRAY_SIZE(vcpu->arch.gpr); i++)
-               vcpu->arch.gpr[i] = regs->gpr[i];
+       for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
+               kvmppc_set_gpr(vcpu, i, regs->gpr[i]);
 
        return 0;
 }
index aebc65e93f4b41782d423fad656e0ba154277f8a..cbc790ee192892fcc19f7a7d856fb1a2db6da2b6 100644 (file)
@@ -62,20 +62,20 @@ int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
                case OP_31_XOP_MFMSR:
                        rt = get_rt(inst);
-                       vcpu->arch.gpr[rt] = vcpu->arch.msr;
+                       kvmppc_set_gpr(vcpu, rt, vcpu->arch.msr);
                        kvmppc_set_exit_type(vcpu, EMULATED_MFMSR_EXITS);
                        break;
 
                case OP_31_XOP_MTMSR:
                        rs = get_rs(inst);
                        kvmppc_set_exit_type(vcpu, EMULATED_MTMSR_EXITS);
-                       kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
+                       kvmppc_set_msr(vcpu, kvmppc_get_gpr(vcpu, rs));
                        break;
 
                case OP_31_XOP_WRTEE:
                        rs = get_rs(inst);
                        vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
-                                                        | (vcpu->arch.gpr[rs] & MSR_EE);
+                                       | (kvmppc_get_gpr(vcpu, rs) & MSR_EE);
                        kvmppc_set_exit_type(vcpu, EMULATED_WRTEE_EXITS);
                        break;
 
@@ -101,22 +101,23 @@ int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
 int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 {
        int emulated = EMULATE_DONE;
+       ulong spr_val = kvmppc_get_gpr(vcpu, rs);
 
        switch (sprn) {
        case SPRN_DEAR:
-               vcpu->arch.dear = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.dear = spr_val; break;
        case SPRN_ESR:
-               vcpu->arch.esr = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.esr = spr_val; break;
        case SPRN_DBCR0:
-               vcpu->arch.dbcr0 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.dbcr0 = spr_val; break;
        case SPRN_DBCR1:
-               vcpu->arch.dbcr1 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.dbcr1 = spr_val; break;
        case SPRN_DBSR:
-               vcpu->arch.dbsr &= ~vcpu->arch.gpr[rs]; break;
+               vcpu->arch.dbsr &= ~spr_val; break;
        case SPRN_TSR:
-               vcpu->arch.tsr &= ~vcpu->arch.gpr[rs]; break;
+               vcpu->arch.tsr &= ~spr_val; break;
        case SPRN_TCR:
-               vcpu->arch.tcr = vcpu->arch.gpr[rs];
+               vcpu->arch.tcr = spr_val;
                kvmppc_emulate_dec(vcpu);
                break;
 
@@ -124,64 +125,64 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
         * loaded into the real SPRGs when resuming the
         * guest. */
        case SPRN_SPRG4:
-               vcpu->arch.sprg4 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.sprg4 = spr_val; break;
        case SPRN_SPRG5:
-               vcpu->arch.sprg5 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.sprg5 = spr_val; break;
        case SPRN_SPRG6:
-               vcpu->arch.sprg6 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.sprg6 = spr_val; break;
        case SPRN_SPRG7:
-               vcpu->arch.sprg7 = vcpu->arch.gpr[rs]; break;
+               vcpu->arch.sprg7 = spr_val; break;
 
        case SPRN_IVPR:
-               vcpu->arch.ivpr = vcpu->arch.gpr[rs];
+               vcpu->arch.ivpr = spr_val;
                break;
        case SPRN_IVOR0:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = spr_val;
                break;
        case SPRN_IVOR1:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_MACHINE_CHECK] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_MACHINE_CHECK] = spr_val;
                break;
        case SPRN_IVOR2:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = spr_val;
                break;
        case SPRN_IVOR3:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = spr_val;
                break;
        case SPRN_IVOR4:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_EXTERNAL] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_EXTERNAL] = spr_val;
                break;
        case SPRN_IVOR5:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_ALIGNMENT] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_ALIGNMENT] = spr_val;
                break;
        case SPRN_IVOR6:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_PROGRAM] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_PROGRAM] = spr_val;
                break;
        case SPRN_IVOR7:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_FP_UNAVAIL] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_FP_UNAVAIL] = spr_val;
                break;
        case SPRN_IVOR8:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = spr_val;
                break;
        case SPRN_IVOR9:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = spr_val;
                break;
        case SPRN_IVOR10:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_DECREMENTER] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_DECREMENTER] = spr_val;
                break;
        case SPRN_IVOR11:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_FIT] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_FIT] = spr_val;
                break;
        case SPRN_IVOR12:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_WATCHDOG] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_WATCHDOG] = spr_val;
                break;
        case SPRN_IVOR13:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS] = spr_val;
                break;
        case SPRN_IVOR14:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS] = spr_val;
                break;
        case SPRN_IVOR15:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG] = spr_val;
                break;
 
        default:
@@ -197,65 +198,65 @@ int kvmppc_booke_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
 
        switch (sprn) {
        case SPRN_IVPR:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivpr; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivpr); break;
        case SPRN_DEAR:
-               vcpu->arch.gpr[rt] = vcpu->arch.dear; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dear); break;
        case SPRN_ESR:
-               vcpu->arch.gpr[rt] = vcpu->arch.esr; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.esr); break;
        case SPRN_DBCR0:
-               vcpu->arch.gpr[rt] = vcpu->arch.dbcr0; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dbcr0); break;
        case SPRN_DBCR1:
-               vcpu->arch.gpr[rt] = vcpu->arch.dbcr1; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dbcr1); break;
        case SPRN_DBSR:
-               vcpu->arch.gpr[rt] = vcpu->arch.dbsr; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dbsr); break;
 
        case SPRN_IVOR0:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL]);
                break;
        case SPRN_IVOR1:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_MACHINE_CHECK];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_MACHINE_CHECK]);
                break;
        case SPRN_IVOR2:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE]);
                break;
        case SPRN_IVOR3:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE]);
                break;
        case SPRN_IVOR4:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_EXTERNAL];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_EXTERNAL]);
                break;
        case SPRN_IVOR5:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_ALIGNMENT];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_ALIGNMENT]);
                break;
        case SPRN_IVOR6:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_PROGRAM];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_PROGRAM]);
                break;
        case SPRN_IVOR7:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_FP_UNAVAIL];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_FP_UNAVAIL]);
                break;
        case SPRN_IVOR8:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL]);
                break;
        case SPRN_IVOR9:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL]);
                break;
        case SPRN_IVOR10:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_DECREMENTER];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_DECREMENTER]);
                break;
        case SPRN_IVOR11:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_FIT];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_FIT]);
                break;
        case SPRN_IVOR12:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_WATCHDOG];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_WATCHDOG]);
                break;
        case SPRN_IVOR13:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_DTLB_MISS]);
                break;
        case SPRN_IVOR14:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_ITLB_MISS]);
                break;
        case SPRN_IVOR15:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_DEBUG]);
                break;
 
        default:
index 64949eef43f12994f9d75ee7eb86eb11d03b2d5b..efa1198940ab6b550a9f2e9ccf99d88692e43e61 100644 (file)
@@ -60,6 +60,12 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
 
        kvmppc_e500_tlb_setup(vcpu_e500);
 
+       /* Registers init */
+       vcpu->arch.pvr = mfspr(SPRN_PVR);
+
+       /* Since booke kvm only support one core, update all vcpus' PIR to 0 */
+       vcpu->vcpu_id = 0;
+
        return 0;
 }
 
index be95b8d8e3b78506491f9075cc9e1a0146faeff3..8e3edfbc963412e39813dbb7ccff1b8a57bdcfd0 100644 (file)
@@ -74,54 +74,59 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 {
        struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu);
        int emulated = EMULATE_DONE;
+       ulong spr_val = kvmppc_get_gpr(vcpu, rs);
 
        switch (sprn) {
        case SPRN_PID:
                vcpu_e500->pid[0] = vcpu->arch.shadow_pid =
-                       vcpu->arch.pid = vcpu->arch.gpr[rs];
+                       vcpu->arch.pid = spr_val;
                break;
        case SPRN_PID1:
-               vcpu_e500->pid[1] = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->pid[1] = spr_val; break;
        case SPRN_PID2:
-               vcpu_e500->pid[2] = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->pid[2] = spr_val; break;
        case SPRN_MAS0:
-               vcpu_e500->mas0 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas0 = spr_val; break;
        case SPRN_MAS1:
-               vcpu_e500->mas1 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas1 = spr_val; break;
        case SPRN_MAS2:
-               vcpu_e500->mas2 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas2 = spr_val; break;
        case SPRN_MAS3:
-               vcpu_e500->mas3 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas3 = spr_val; break;
        case SPRN_MAS4:
-               vcpu_e500->mas4 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas4 = spr_val; break;
        case SPRN_MAS6:
-               vcpu_e500->mas6 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas6 = spr_val; break;
        case SPRN_MAS7:
-               vcpu_e500->mas7 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->mas7 = spr_val; break;
+       case SPRN_L1CSR0:
+               vcpu_e500->l1csr0 = spr_val;
+               vcpu_e500->l1csr0 &= ~(L1CSR0_DCFI | L1CSR0_CLFC);
+               break;
        case SPRN_L1CSR1:
-               vcpu_e500->l1csr1 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->l1csr1 = spr_val; break;
        case SPRN_HID0:
-               vcpu_e500->hid0 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->hid0 = spr_val; break;
        case SPRN_HID1:
-               vcpu_e500->hid1 = vcpu->arch.gpr[rs]; break;
+               vcpu_e500->hid1 = spr_val; break;
 
        case SPRN_MMUCSR0:
                emulated = kvmppc_e500_emul_mt_mmucsr0(vcpu_e500,
-                               vcpu->arch.gpr[rs]);
+                               spr_val);
                break;
 
        /* extra exceptions */
        case SPRN_IVOR32:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_UNAVAIL] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_UNAVAIL] = spr_val;
                break;
        case SPRN_IVOR33:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_DATA] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_DATA] = spr_val;
                break;
        case SPRN_IVOR34:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_ROUND] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_ROUND] = spr_val;
                break;
        case SPRN_IVOR35:
-               vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR] = vcpu->arch.gpr[rs];
+               vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR] = spr_val;
                break;
 
        default:
@@ -138,63 +143,57 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
 
        switch (sprn) {
        case SPRN_PID:
-               vcpu->arch.gpr[rt] = vcpu_e500->pid[0]; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->pid[0]); break;
        case SPRN_PID1:
-               vcpu->arch.gpr[rt] = vcpu_e500->pid[1]; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->pid[1]); break;
        case SPRN_PID2:
-               vcpu->arch.gpr[rt] = vcpu_e500->pid[2]; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->pid[2]); break;
        case SPRN_MAS0:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas0; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas0); break;
        case SPRN_MAS1:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas1; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas1); break;
        case SPRN_MAS2:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas2; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas2); break;
        case SPRN_MAS3:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas3; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas3); break;
        case SPRN_MAS4:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas4; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas4); break;
        case SPRN_MAS6:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas6; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas6); break;
        case SPRN_MAS7:
-               vcpu->arch.gpr[rt] = vcpu_e500->mas7; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->mas7); break;
 
        case SPRN_TLB0CFG:
-               vcpu->arch.gpr[rt] = mfspr(SPRN_TLB0CFG);
-               vcpu->arch.gpr[rt] &= ~0xfffUL;
-               vcpu->arch.gpr[rt] |= vcpu_e500->guest_tlb_size[0];
-               break;
-
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->tlb0cfg); break;
        case SPRN_TLB1CFG:
-               vcpu->arch.gpr[rt] = mfspr(SPRN_TLB1CFG);
-               vcpu->arch.gpr[rt] &= ~0xfffUL;
-               vcpu->arch.gpr[rt] |= vcpu_e500->guest_tlb_size[1];
-               break;
-
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->tlb1cfg); break;
+       case SPRN_L1CSR0:
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->l1csr0); break;
        case SPRN_L1CSR1:
-               vcpu->arch.gpr[rt] = vcpu_e500->l1csr1; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->l1csr1); break;
        case SPRN_HID0:
-               vcpu->arch.gpr[rt] = vcpu_e500->hid0; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->hid0); break;
        case SPRN_HID1:
-               vcpu->arch.gpr[rt] = vcpu_e500->hid1; break;
+               kvmppc_set_gpr(vcpu, rt, vcpu_e500->hid1); break;
 
        case SPRN_MMUCSR0:
-               vcpu->arch.gpr[rt] = 0; break;
+               kvmppc_set_gpr(vcpu, rt, 0); break;
 
        case SPRN_MMUCFG:
-               vcpu->arch.gpr[rt] = mfspr(SPRN_MMUCFG); break;
+               kvmppc_set_gpr(vcpu, rt, mfspr(SPRN_MMUCFG)); break;
 
        /* extra exceptions */
        case SPRN_IVOR32:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_UNAVAIL];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_UNAVAIL]);
                break;
        case SPRN_IVOR33:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_DATA];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_DATA]);
                break;
        case SPRN_IVOR34:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_ROUND];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_ROUND]);
                break;
        case SPRN_IVOR35:
-               vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR];
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR]);
                break;
        default:
                emulated = kvmppc_booke_emulate_mfspr(vcpu, sprn, rt);
index fb1e1dc11ba5e9fed05d4dfa82dc979f3389bb49..0d772e6b6318cf30f081b5ec76c9bfe035ca5250 100644 (file)
@@ -417,7 +417,7 @@ int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb)
        int esel, tlbsel;
        gva_t ea;
 
-       ea = ((ra) ? vcpu->arch.gpr[ra] : 0) + vcpu->arch.gpr[rb];
+       ea = ((ra) ? kvmppc_get_gpr(vcpu, ra) : 0) + kvmppc_get_gpr(vcpu, rb);
 
        ia = (ea >> 2) & 0x1;
 
@@ -470,7 +470,7 @@ int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *vcpu, int rb)
        struct tlbe *gtlbe = NULL;
        gva_t ea;
 
-       ea = vcpu->arch.gpr[rb];
+       ea = kvmppc_get_gpr(vcpu, rb);
 
        for (tlbsel = 0; tlbsel < 2; tlbsel++) {
                esel = kvmppc_e500_tlb_index(vcpu_e500, ea, tlbsel, pid, as);
@@ -728,6 +728,12 @@ int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
        if (vcpu_e500->shadow_pages[1] == NULL)
                goto err_out_page0;
 
+       /* Init TLB configuration register */
+       vcpu_e500->tlb0cfg = mfspr(SPRN_TLB0CFG) & ~0xfffUL;
+       vcpu_e500->tlb0cfg |= vcpu_e500->guest_tlb_size[0];
+       vcpu_e500->tlb1cfg = mfspr(SPRN_TLB1CFG) & ~0xfffUL;
+       vcpu_e500->tlb1cfg |= vcpu_e500->guest_tlb_size[1];
+
        return 0;
 
 err_out_page0:
index 4a9ac6640fadb93182d9ade73310f1af1e0c4b93..cb72a65f4eccc01bcac5a431a090df8f36a9a374 100644 (file)
@@ -83,6 +83,9 @@ void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
 
        pr_debug("mtDEC: %x\n", vcpu->arch.dec);
 #ifdef CONFIG_PPC64
+       /* mtdec lowers the interrupt line when positive. */
+       kvmppc_core_dequeue_dec(vcpu);
+
        /* POWER4+ triggers a dec interrupt if the value is < 0 */
        if (vcpu->arch.dec & 0x80000000) {
                hrtimer_try_to_cancel(&vcpu->arch.dec_timer);
@@ -140,14 +143,18 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
 
        pr_debug(KERN_INFO "Emulating opcode %d / %d\n", get_op(inst), get_xop(inst));
 
+       /* Try again next time */
+       if (inst == KVM_INST_FETCH_FAILED)
+               return EMULATE_DONE;
+
        switch (get_op(inst)) {
        case OP_TRAP:
 #ifdef CONFIG_PPC64
        case OP_TRAP_64:
+               kvmppc_core_queue_program(vcpu, SRR1_PROGTRAP);
 #else
-               vcpu->arch.esr |= ESR_PTR;
+               kvmppc_core_queue_program(vcpu, vcpu->arch.esr | ESR_PTR);
 #endif
-               kvmppc_core_queue_program(vcpu);
                advance = 0;
                break;
 
@@ -167,14 +174,14 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                case OP_31_XOP_STWX:
                        rs = get_rs(inst);
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       4, 1);
                        break;
 
                case OP_31_XOP_STBX:
                        rs = get_rs(inst);
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       1, 1);
                        break;
 
@@ -183,14 +190,14 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        ra = get_ra(inst);
                        rb = get_rb(inst);
 
-                       ea = vcpu->arch.gpr[rb];
+                       ea = kvmppc_get_gpr(vcpu, rb);
                        if (ra)
-                               ea += vcpu->arch.gpr[ra];
+                               ea += kvmppc_get_gpr(vcpu, ra);
 
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       1, 1);
-                       vcpu->arch.gpr[rs] = ea;
+                       kvmppc_set_gpr(vcpu, rs, ea);
                        break;
 
                case OP_31_XOP_LHZX:
@@ -203,12 +210,12 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        ra = get_ra(inst);
                        rb = get_rb(inst);
 
-                       ea = vcpu->arch.gpr[rb];
+                       ea = kvmppc_get_gpr(vcpu, rb);
                        if (ra)
-                               ea += vcpu->arch.gpr[ra];
+                               ea += kvmppc_get_gpr(vcpu, ra);
 
                        emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
-                       vcpu->arch.gpr[ra] = ea;
+                       kvmppc_set_gpr(vcpu, ra, ea);
                        break;
 
                case OP_31_XOP_MFSPR:
@@ -217,47 +224,49 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
 
                        switch (sprn) {
                        case SPRN_SRR0:
-                               vcpu->arch.gpr[rt] = vcpu->arch.srr0; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.srr0); break;
                        case SPRN_SRR1:
-                               vcpu->arch.gpr[rt] = vcpu->arch.srr1; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.srr1); break;
                        case SPRN_PVR:
-                               vcpu->arch.gpr[rt] = vcpu->arch.pvr; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.pvr); break;
                        case SPRN_PIR:
-                               vcpu->arch.gpr[rt] = vcpu->vcpu_id; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->vcpu_id); break;
                        case SPRN_MSSSR0:
-                               vcpu->arch.gpr[rt] = 0; break;
+                               kvmppc_set_gpr(vcpu, rt, 0); break;
 
                        /* Note: mftb and TBRL/TBWL are user-accessible, so
                         * the guest can always access the real TB anyways.
                         * In fact, we probably will never see these traps. */
                        case SPRN_TBWL:
-                               vcpu->arch.gpr[rt] = get_tb() >> 32; break;
+                               kvmppc_set_gpr(vcpu, rt, get_tb() >> 32); break;
                        case SPRN_TBWU:
-                               vcpu->arch.gpr[rt] = get_tb(); break;
+                               kvmppc_set_gpr(vcpu, rt, get_tb()); break;
 
                        case SPRN_SPRG0:
-                               vcpu->arch.gpr[rt] = vcpu->arch.sprg0; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.sprg0); break;
                        case SPRN_SPRG1:
-                               vcpu->arch.gpr[rt] = vcpu->arch.sprg1; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.sprg1); break;
                        case SPRN_SPRG2:
-                               vcpu->arch.gpr[rt] = vcpu->arch.sprg2; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.sprg2); break;
                        case SPRN_SPRG3:
-                               vcpu->arch.gpr[rt] = vcpu->arch.sprg3; break;
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.sprg3); break;
                        /* Note: SPRG4-7 are user-readable, so we don't get
                         * a trap. */
 
                        case SPRN_DEC:
                        {
                                u64 jd = get_tb() - vcpu->arch.dec_jiffies;
-                               vcpu->arch.gpr[rt] = vcpu->arch.dec - jd;
-                               pr_debug(KERN_INFO "mfDEC: %x - %llx = %lx\n", vcpu->arch.dec, jd, vcpu->arch.gpr[rt]);
+                               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dec - jd);
+                               pr_debug(KERN_INFO "mfDEC: %x - %llx = %lx\n",
+                                        vcpu->arch.dec, jd,
+                                        kvmppc_get_gpr(vcpu, rt));
                                break;
                        }
                        default:
                                emulated = kvmppc_core_emulate_mfspr(vcpu, sprn, rt);
                                if (emulated == EMULATE_FAIL) {
                                        printk("mfspr: unknown spr %x\n", sprn);
-                                       vcpu->arch.gpr[rt] = 0;
+                                       kvmppc_set_gpr(vcpu, rt, 0);
                                }
                                break;
                        }
@@ -269,7 +278,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        rb = get_rb(inst);
 
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       2, 1);
                        break;
 
@@ -278,14 +287,14 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        ra = get_ra(inst);
                        rb = get_rb(inst);
 
-                       ea = vcpu->arch.gpr[rb];
+                       ea = kvmppc_get_gpr(vcpu, rb);
                        if (ra)
-                               ea += vcpu->arch.gpr[ra];
+                               ea += kvmppc_get_gpr(vcpu, ra);
 
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       2, 1);
-                       vcpu->arch.gpr[ra] = ea;
+                       kvmppc_set_gpr(vcpu, ra, ea);
                        break;
 
                case OP_31_XOP_MTSPR:
@@ -293,9 +302,9 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        rs = get_rs(inst);
                        switch (sprn) {
                        case SPRN_SRR0:
-                               vcpu->arch.srr0 = vcpu->arch.gpr[rs]; break;
+                               vcpu->arch.srr0 = kvmppc_get_gpr(vcpu, rs); break;
                        case SPRN_SRR1:
-                               vcpu->arch.srr1 = vcpu->arch.gpr[rs]; break;
+                               vcpu->arch.srr1 = kvmppc_get_gpr(vcpu, rs); break;
 
                        /* XXX We need to context-switch the timebase for
                         * watchdog and FIT. */
@@ -305,18 +314,18 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        case SPRN_MSSSR0: break;
 
                        case SPRN_DEC:
-                               vcpu->arch.dec = vcpu->arch.gpr[rs];
+                               vcpu->arch.dec = kvmppc_get_gpr(vcpu, rs);
                                kvmppc_emulate_dec(vcpu);
                                break;
 
                        case SPRN_SPRG0:
-                               vcpu->arch.sprg0 = vcpu->arch.gpr[rs]; break;
+                               vcpu->arch.sprg0 = kvmppc_get_gpr(vcpu, rs); break;
                        case SPRN_SPRG1:
-                               vcpu->arch.sprg1 = vcpu->arch.gpr[rs]; break;
+                               vcpu->arch.sprg1 = kvmppc_get_gpr(vcpu, rs); break;
                        case SPRN_SPRG2:
-                               vcpu->arch.sprg2 = vcpu->arch.gpr[rs]; break;
+                               vcpu->arch.sprg2 = kvmppc_get_gpr(vcpu, rs); break;
                        case SPRN_SPRG3:
-                               vcpu->arch.sprg3 = vcpu->arch.gpr[rs]; break;
+                               vcpu->arch.sprg3 = kvmppc_get_gpr(vcpu, rs); break;
 
                        default:
                                emulated = kvmppc_core_emulate_mtspr(vcpu, sprn, rs);
@@ -348,7 +357,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        rb = get_rb(inst);
 
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       4, 0);
                        break;
 
@@ -363,7 +372,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        rb = get_rb(inst);
 
                        emulated = kvmppc_handle_store(run, vcpu,
-                                                      vcpu->arch.gpr[rs],
+                                                      kvmppc_get_gpr(vcpu, rs),
                                                       2, 0);
                        break;
 
@@ -382,7 +391,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                ra = get_ra(inst);
                rt = get_rt(inst);
                emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
-               vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed;
+               kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed);
                break;
 
        case OP_LBZ:
@@ -394,35 +403,39 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                ra = get_ra(inst);
                rt = get_rt(inst);
                emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
-               vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed;
+               kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed);
                break;
 
        case OP_STW:
                rs = get_rs(inst);
-               emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs],
+               emulated = kvmppc_handle_store(run, vcpu,
+                                              kvmppc_get_gpr(vcpu, rs),
                                               4, 1);
                break;
 
        case OP_STWU:
                ra = get_ra(inst);
                rs = get_rs(inst);
-               emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs],
+               emulated = kvmppc_handle_store(run, vcpu,
+                                              kvmppc_get_gpr(vcpu, rs),
                                               4, 1);
-               vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed;
+               kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed);
                break;
 
        case OP_STB:
                rs = get_rs(inst);
-               emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs],
+               emulated = kvmppc_handle_store(run, vcpu,
+                                              kvmppc_get_gpr(vcpu, rs),
                                               1, 1);
                break;
 
        case OP_STBU:
                ra = get_ra(inst);
                rs = get_rs(inst);
-               emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs],
+               emulated = kvmppc_handle_store(run, vcpu,
+                                              kvmppc_get_gpr(vcpu, rs),
                                               1, 1);
-               vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed;
+               kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed);
                break;
 
        case OP_LHZ:
@@ -434,21 +447,23 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                ra = get_ra(inst);
                rt = get_rt(inst);
                emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
-               vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed;
+               kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed);
                break;
 
        case OP_STH:
                rs = get_rs(inst);
-               emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs],
+               emulated = kvmppc_handle_store(run, vcpu,
+                                              kvmppc_get_gpr(vcpu, rs),
                                               2, 1);
                break;
 
        case OP_STHU:
                ra = get_ra(inst);
                rs = get_rs(inst);
-               emulated = kvmppc_handle_store(run, vcpu, vcpu->arch.gpr[rs],
+               emulated = kvmppc_handle_store(run, vcpu,
+                                              kvmppc_get_gpr(vcpu, rs),
                                               2, 1);
-               vcpu->arch.gpr[ra] = vcpu->arch.paddr_accessed;
+               kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed);
                break;
 
        default:
@@ -461,6 +476,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
                        advance = 0;
                        printk(KERN_ERR "Couldn't emulate instruction 0x%08x "
                               "(op %d xop %d)\n", inst, get_op(inst), get_xop(inst));
+                       kvmppc_core_queue_program(vcpu, 0);
                }
        }
 
index f06cf93b178ec2d037a39eeee202b567a42d6eb2..51aedd7f16bcb14d5d2f073da2fc55a53ec18575 100644 (file)
@@ -137,6 +137,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 {
        kvmppc_free_vcpus(kvm);
        kvm_free_physmem(kvm);
+       cleanup_srcu_struct(&kvm->srcu);
        kfree(kvm);
 }
 
@@ -165,14 +166,24 @@ long kvm_arch_dev_ioctl(struct file *filp,
        return -EINVAL;
 }
 
-int kvm_arch_set_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               struct kvm_memory_slot old,
-                               int user_alloc)
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                                   struct kvm_memory_slot *memslot,
+                                   struct kvm_memory_slot old,
+                                   struct kvm_userspace_memory_region *mem,
+                                   int user_alloc)
 {
        return 0;
 }
 
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+               struct kvm_userspace_memory_region *mem,
+               struct kvm_memory_slot old,
+               int user_alloc)
+{
+       return;
+}
+
+
 void kvm_arch_flush_shadow(struct kvm *kvm)
 {
 }
@@ -260,34 +271,35 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
 static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu,
                                      struct kvm_run *run)
 {
-       ulong *gpr = &vcpu->arch.gpr[vcpu->arch.io_gpr];
-       *gpr = run->dcr.data;
+       kvmppc_set_gpr(vcpu, vcpu->arch.io_gpr, run->dcr.data);
 }
 
 static void kvmppc_complete_mmio_load(struct kvm_vcpu *vcpu,
                                       struct kvm_run *run)
 {
-       ulong *gpr = &vcpu->arch.gpr[vcpu->arch.io_gpr];
+       ulong gpr;
 
-       if (run->mmio.len > sizeof(*gpr)) {
+       if (run->mmio.len > sizeof(gpr)) {
                printk(KERN_ERR "bad MMIO length: %d\n", run->mmio.len);
                return;
        }
 
        if (vcpu->arch.mmio_is_bigendian) {
                switch (run->mmio.len) {
-               case 4: *gpr = *(u32 *)run->mmio.data; break;
-               case 2: *gpr = *(u16 *)run->mmio.data; break;
-               case 1: *gpr = *(u8 *)run->mmio.data; break;
+               case 4: gpr = *(u32 *)run->mmio.data; break;
+               case 2: gpr = *(u16 *)run->mmio.data; break;
+               case 1: gpr = *(u8 *)run->mmio.data; break;
                }
        } else {
                /* Convert BE data from userland back to LE. */
                switch (run->mmio.len) {
-               case 4: *gpr = ld_le32((u32 *)run->mmio.data); break;
-               case 2: *gpr = ld_le16((u16 *)run->mmio.data); break;
-               case 1: *gpr = *(u8 *)run->mmio.data; break;
+               case 4: gpr = ld_le32((u32 *)run->mmio.data); break;
+               case 2: gpr = ld_le16((u16 *)run->mmio.data); break;
+               case 1: gpr = *(u8 *)run->mmio.data; break;
                }
        }
+
+       kvmppc_set_gpr(vcpu, vcpu->arch.io_gpr, gpr);
 }
 
 int kvmppc_handle_load(struct kvm_run *run, struct kvm_vcpu *vcpu,
index b037d95eeadcc0380b34901f8113518f3bf53fa7..64c00227b99786cab5ae38d6513c73db1b13fd8f 100644 (file)
@@ -451,7 +451,7 @@ static int __cpuinit numa_setup_cpu(unsigned long lcpu)
        nid = of_node_to_nid_single(cpu);
 
        if (nid < 0 || !node_online(nid))
-               nid = any_online_node(NODE_MASK_ALL);
+               nid = first_online_node;
 out:
        map_cpu_to_node(lcpu, nid);
 
@@ -1114,7 +1114,7 @@ int hot_add_scn_to_nid(unsigned long scn_addr)
        int nid, found = 0;
 
        if (!numa_enabled || (min_common_depth < 0))
-               return any_online_node(NODE_MASK_ALL);
+               return first_online_node;
 
        memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
        if (memory) {
@@ -1125,7 +1125,7 @@ int hot_add_scn_to_nid(unsigned long scn_addr)
        }
 
        if (nid < 0 || !node_online(nid))
-               nid = any_online_node(NODE_MASK_ALL);
+               nid = first_online_node;
 
        if (NODE_DATA(nid)->node_spanned_pages)
                return nid;
index 341aff2687a5f7453781d3483462e1e4b9c2863a..cd128b07beda7e1c798043c984d4343aa06ae046 100644 (file)
@@ -288,46 +288,30 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = HYPFS_MAGIC;
        sb->s_op = &hypfs_s_ops;
-       if (hypfs_parse_options(data, sb)) {
-               rc = -EINVAL;
-               goto err_alloc;
-       }
+       if (hypfs_parse_options(data, sb))
+               return -EINVAL;
        root_inode = hypfs_make_inode(sb, S_IFDIR | 0755);
-       if (!root_inode) {
-               rc = -ENOMEM;
-               goto err_alloc;
-       }
+       if (!root_inode)
+               return -ENOMEM;
        root_inode->i_op = &simple_dir_inode_operations;
        root_inode->i_fop = &simple_dir_operations;
-       root_dentry = d_alloc_root(root_inode);
+       sb->s_root = root_dentry = d_alloc_root(root_inode);
        if (!root_dentry) {
                iput(root_inode);
-               rc = -ENOMEM;
-               goto err_alloc;
+               return -ENOMEM;
        }
        if (MACHINE_IS_VM)
                rc = hypfs_vm_create_files(sb, root_dentry);
        else
                rc = hypfs_diag_create_files(sb, root_dentry);
        if (rc)
-               goto err_tree;
+               return rc;
        sbi->update_file = hypfs_create_update_file(sb, root_dentry);
-       if (IS_ERR(sbi->update_file)) {
-               rc = PTR_ERR(sbi->update_file);
-               goto err_tree;
-       }
+       if (IS_ERR(sbi->update_file))
+               return PTR_ERR(sbi->update_file);
        hypfs_update_update(sb);
-       sb->s_root = root_dentry;
        pr_info("Hypervisor filesystem mounted\n");
        return 0;
-
-err_tree:
-       hypfs_delete_tree(root_dentry);
-       d_genocide(root_dentry);
-       dput(root_dentry);
-err_alloc:
-       kfree(sbi);
-       return rc;
 }
 
 static int hypfs_get_super(struct file_system_type *fst, int flags,
@@ -340,12 +324,12 @@ static void hypfs_kill_super(struct super_block *sb)
 {
        struct hypfs_sb_info *sb_info = sb->s_fs_info;
 
-       if (sb->s_root) {
+       if (sb->s_root)
                hypfs_delete_tree(sb->s_root);
+       if (sb_info->update_file)
                hypfs_remove(sb_info->update_file);
-               kfree(sb->s_fs_info);
-               sb->s_fs_info = NULL;
-       }
+       kfree(sb->s_fs_info);
+       sb->s_fs_info = NULL;
        kill_litter_super(sb);
 }
 
index 3fa0a10e4668f1ec5df6b0a892ba11b4131cd7c9..49292869a5cdbeb18cb2a3a17e46a25f68d21b55 100644 (file)
@@ -242,6 +242,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
        kvm_free_physmem(kvm);
        free_page((unsigned long)(kvm->arch.sca));
        debug_unregister(kvm->arch.dbf);
+       cleanup_srcu_struct(&kvm->srcu);
        kfree(kvm);
 }
 
@@ -690,14 +691,12 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
 }
 
 /* Section: memory related */
-int kvm_arch_set_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               struct kvm_memory_slot old,
-                               int user_alloc)
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                                  struct kvm_memory_slot *memslot,
+                                  struct kvm_memory_slot old,
+                                  struct kvm_userspace_memory_region *mem,
+                                  int user_alloc)
 {
-       int i;
-       struct kvm_vcpu *vcpu;
-
        /* A few sanity checks. We can have exactly one memory slot which has
           to start at guest virtual zero and which has to be located at a
           page boundary in userland and which has to end at a page boundary.
@@ -720,14 +719,23 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
        if (!user_alloc)
                return -EINVAL;
 
+       return 0;
+}
+
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+                               struct kvm_userspace_memory_region *mem,
+                               struct kvm_memory_slot old,
+                               int user_alloc)
+{
+       int i;
+       struct kvm_vcpu *vcpu;
+
        /* request update of sie control block for all available vcpus */
        kvm_for_each_vcpu(i, vcpu, kvm) {
                if (test_and_set_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests))
                        continue;
                kvm_s390_inject_sigp_stop(vcpu, ACTION_RELOADVCPU_ON_STOP);
        }
-
-       return 0;
 }
 
 void kvm_arch_flush_shadow(struct kvm *kvm)
index 06cce8285ba01883ee2e4f97a126ff6b50ea11d5..60f09ab3672c9d26453395f70addbb45f6a77d6c 100644 (file)
@@ -67,10 +67,14 @@ static inline long kvm_s390_vcpu_get_memsize(struct kvm_vcpu *vcpu)
 
 static inline void kvm_s390_vcpu_set_mem(struct kvm_vcpu *vcpu)
 {
+       int idx;
        struct kvm_memory_slot *mem;
+       struct kvm_memslots *memslots;
 
-       down_read(&vcpu->kvm->slots_lock);
-       mem = &vcpu->kvm->memslots[0];
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
+       memslots = rcu_dereference(vcpu->kvm->memslots);
+
+       mem = &memslots->memslots[0];
 
        vcpu->arch.sie_block->gmsor = mem->userspace_addr;
        vcpu->arch.sie_block->gmslm =
@@ -78,7 +82,7 @@ static inline void kvm_s390_vcpu_set_mem(struct kvm_vcpu *vcpu)
                (mem->npages << PAGE_SHIFT) +
                VIRTIODESCSPACE - 1ul;
 
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
 }
 
 /* implemented in priv.c */
index be300aaca6fed6ceea4c8ddfe64c3b3190f1384e..7da0fc94a01efc34aecf31b4909e98f9532faf13 100644 (file)
@@ -419,6 +419,9 @@ static struct i2c_board_info migor_i2c_devices[] = {
                I2C_BOARD_INFO("migor_ts", 0x51),
                .irq = 38, /* IRQ6 */
        },
+       {
+               I2C_BOARD_INFO("wm8978", 0x1a),
+       },
 };
 
 static struct i2c_board_info migor_i2c_camera[] = {
@@ -619,6 +622,19 @@ static int __init migor_devices_setup(void)
 
        platform_resource_setup_memory(&migor_ceu_device, "ceu", 4 << 20);
 
+       /* SIU: Port B */
+       gpio_request(GPIO_FN_SIUBOLR, NULL);
+       gpio_request(GPIO_FN_SIUBOBT, NULL);
+       gpio_request(GPIO_FN_SIUBISLD, NULL);
+       gpio_request(GPIO_FN_SIUBOSLD, NULL);
+       gpio_request(GPIO_FN_SIUMCKB, NULL);
+
+       /*
+        * The original driver sets SIUB OLR/OBT, ILR/IBT, and SIUA OLR/OBT to
+        * output. Need only SIUB, set to output for master mode (table 34.2)
+        */
+       __raw_writew(__raw_readw(PORT_MSELCRA) | 1, PORT_MSELCRA);
+
        i2c_register_board_info(0, migor_i2c_devices,
                                ARRAY_SIZE(migor_i2c_devices));
 
index e27fc74f228c0a92a366a1ad7314d284e1a3e024..d0b77b68a4d0d95d74db19a0172b6f126a214c61 100644 (file)
@@ -5,7 +5,7 @@ int cache_control(unsigned int command)
 
        for (i = 0; i < (32 * 1024); i += 32) {
                (void)*p;
-               p += (32 / sizeof (int));
+               p += (32 / sizeof(int));
        }
 
        return 0;
index da3ebec921a70db7941f3bfac3f47261c759bc76..1f4e562c5e8cc1317bd97a448e5067465bad26a9 100644 (file)
@@ -86,8 +86,8 @@ extern void copy_from_user_page(struct vm_area_struct *vma,
        struct page *page, unsigned long vaddr, void *dst, const void *src,
        unsigned long len);
 
-#define flush_cache_vmap(start, end)           flush_cache_all()
-#define flush_cache_vunmap(start, end)         flush_cache_all()
+#define flush_cache_vmap(start, end)           local_flush_cache_all(NULL)
+#define flush_cache_vunmap(start, end)         local_flush_cache_all(NULL)
 
 #define flush_dcache_mmap_lock(mapping)                do { } while (0)
 #define flush_dcache_mmap_unlock(mapping)      do { } while (0)
diff --git a/arch/sh/include/asm/dma-register.h b/arch/sh/include/asm/dma-register.h
new file mode 100644 (file)
index 0000000..51cd78f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Common header for the legacy SH DMA driver and the new dmaengine driver
+ *
+ * extracted from arch/sh/include/asm/dma-sh.h:
+ *
+ * Copyright (C) 2000  Takashi YOSHII
+ * Copyright (C) 2003  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef DMA_REGISTER_H
+#define DMA_REGISTER_H
+
+/* DMA register */
+#define SAR    0x00
+#define DAR    0x04
+#define TCR    0x08
+#define CHCR   0x0C
+#define DMAOR  0x40
+
+/* DMAOR definitions */
+#define DMAOR_AE       0x00000004
+#define DMAOR_NMIF     0x00000002
+#define DMAOR_DME      0x00000001
+
+/* Definitions for the SuperH DMAC */
+#define REQ_L  0x00000000
+#define REQ_E  0x00080000
+#define RACK_H 0x00000000
+#define RACK_L 0x00040000
+#define ACK_R  0x00000000
+#define ACK_W  0x00020000
+#define ACK_H  0x00000000
+#define ACK_L  0x00010000
+#define DM_INC 0x00004000
+#define DM_DEC 0x00008000
+#define DM_FIX 0x0000c000
+#define SM_INC 0x00001000
+#define SM_DEC 0x00002000
+#define SM_FIX 0x00003000
+#define RS_IN  0x00000200
+#define RS_OUT 0x00000300
+#define TS_BLK 0x00000040
+#define TM_BUR 0x00000020
+#define CHCR_DE        0x00000001
+#define CHCR_TE        0x00000002
+#define CHCR_IE        0x00000004
+
+#endif
index e934a2e666517ce4dae593928c9791516362efa1..f3acb8e34c6bef0b07e9dc65db0c8adc5a4502ef 100644 (file)
@@ -11,7 +11,8 @@
 #ifndef __DMA_SH_H
 #define __DMA_SH_H
 
-#include <asm/dma.h>
+#include <asm/dma-register.h>
+#include <cpu/dma-register.h>
 #include <cpu/dma.h>
 
 /* DMAOR contorl: The DMAOR access size is different by CPU.*/
@@ -53,34 +54,6 @@ static int dmte_irq_map[] __maybe_unused = {
 #endif
 };
 
-/* Definitions for the SuperH DMAC */
-#define REQ_L  0x00000000
-#define REQ_E  0x00080000
-#define RACK_H 0x00000000
-#define RACK_L 0x00040000
-#define ACK_R  0x00000000
-#define ACK_W  0x00020000
-#define ACK_H  0x00000000
-#define ACK_L  0x00010000
-#define DM_INC 0x00004000
-#define DM_DEC 0x00008000
-#define DM_FIX 0x0000c000
-#define SM_INC 0x00001000
-#define SM_DEC 0x00002000
-#define SM_FIX 0x00003000
-#define RS_IN  0x00000200
-#define RS_OUT 0x00000300
-#define TS_BLK 0x00000040
-#define TM_BUR 0x00000020
-#define CHCR_DE 0x00000001
-#define CHCR_TE 0x00000002
-#define CHCR_IE 0x00000004
-
-/* DMAOR definitions */
-#define DMAOR_AE       0x00000004
-#define DMAOR_NMIF     0x00000002
-#define DMAOR_DME      0x00000001
-
 /*
  * Define the default configuration for dual address memory-memory transfer.
  * The 0x400 value represents auto-request, external->external.
@@ -111,61 +84,4 @@ static u32 dma_base_addr[] __maybe_unused = {
 #endif
 };
 
-/* DMA register */
-#define SAR     0x00
-#define DAR     0x04
-#define TCR     0x08
-#define CHCR    0x0C
-#define DMAOR  0x40
-
-/*
- * for dma engine
- *
- * SuperH DMA mode
- */
-#define SHDMA_MIX_IRQ  (1 << 1)
-#define SHDMA_DMAOR1   (1 << 2)
-#define SHDMA_DMAE1    (1 << 3)
-
-enum sh_dmae_slave_chan_id {
-       SHDMA_SLAVE_SCIF0_TX,
-       SHDMA_SLAVE_SCIF0_RX,
-       SHDMA_SLAVE_SCIF1_TX,
-       SHDMA_SLAVE_SCIF1_RX,
-       SHDMA_SLAVE_SCIF2_TX,
-       SHDMA_SLAVE_SCIF2_RX,
-       SHDMA_SLAVE_SCIF3_TX,
-       SHDMA_SLAVE_SCIF3_RX,
-       SHDMA_SLAVE_SCIF4_TX,
-       SHDMA_SLAVE_SCIF4_RX,
-       SHDMA_SLAVE_SCIF5_TX,
-       SHDMA_SLAVE_SCIF5_RX,
-       SHDMA_SLAVE_SIUA_TX,
-       SHDMA_SLAVE_SIUA_RX,
-       SHDMA_SLAVE_SIUB_TX,
-       SHDMA_SLAVE_SIUB_RX,
-       SHDMA_SLAVE_NUMBER,     /* Must stay last */
-};
-
-struct sh_dmae_slave_config {
-       enum sh_dmae_slave_chan_id      slave_id;
-       dma_addr_t                      addr;
-       u32                             chcr;
-       char                            mid_rid;
-};
-
-struct sh_dmae_pdata {
-       unsigned int mode;
-       struct sh_dmae_slave_config *config;
-       int config_num;
-};
-
-struct device;
-
-struct sh_dmae_slave {
-       enum sh_dmae_slave_chan_id      slave_id; /* Set by the platform */
-       struct device                   *dma_dev; /* Set by the platform */
-       struct sh_dmae_slave_config     *config;  /* Set by the driver */
-};
-
 #endif /* __DMA_SH_H */
diff --git a/arch/sh/include/asm/dmaengine.h b/arch/sh/include/asm/dmaengine.h
new file mode 100644 (file)
index 0000000..bf2f30c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Header for the new SH dmaengine driver
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.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.
+ */
+#ifndef ASM_DMAENGINE_H
+#define ASM_DMAENGINE_H
+
+#include <linux/dmaengine.h>
+#include <linux/list.h>
+
+#include <asm/dma-register.h>
+
+#define SH_DMAC_MAX_CHANNELS   6
+
+enum sh_dmae_slave_chan_id {
+       SHDMA_SLAVE_SCIF0_TX,
+       SHDMA_SLAVE_SCIF0_RX,
+       SHDMA_SLAVE_SCIF1_TX,
+       SHDMA_SLAVE_SCIF1_RX,
+       SHDMA_SLAVE_SCIF2_TX,
+       SHDMA_SLAVE_SCIF2_RX,
+       SHDMA_SLAVE_SCIF3_TX,
+       SHDMA_SLAVE_SCIF3_RX,
+       SHDMA_SLAVE_SCIF4_TX,
+       SHDMA_SLAVE_SCIF4_RX,
+       SHDMA_SLAVE_SCIF5_TX,
+       SHDMA_SLAVE_SCIF5_RX,
+       SHDMA_SLAVE_SIUA_TX,
+       SHDMA_SLAVE_SIUA_RX,
+       SHDMA_SLAVE_SIUB_TX,
+       SHDMA_SLAVE_SIUB_RX,
+       SHDMA_SLAVE_NUMBER,     /* Must stay last */
+};
+
+struct sh_dmae_slave_config {
+       enum sh_dmae_slave_chan_id      slave_id;
+       dma_addr_t                      addr;
+       u32                             chcr;
+       char                            mid_rid;
+};
+
+struct sh_dmae_channel {
+       unsigned int    offset;
+       unsigned int    dmars;
+       unsigned int    dmars_bit;
+};
+
+struct sh_dmae_pdata {
+       struct sh_dmae_slave_config *slave;
+       int slave_num;
+       struct sh_dmae_channel *channel;
+       int channel_num;
+       unsigned int ts_low_shift;
+       unsigned int ts_low_mask;
+       unsigned int ts_high_shift;
+       unsigned int ts_high_mask;
+       unsigned int *ts_shift;
+       int ts_shift_num;
+       u16 dmaor_init;
+};
+
+struct device;
+
+/* Used by slave DMA clients to request DMA to/from a specific peripheral */
+struct sh_dmae_slave {
+       enum sh_dmae_slave_chan_id      slave_id; /* Set by the platform */
+       struct device                   *dma_dev; /* Set by the platform */
+       struct sh_dmae_slave_config     *config;  /* Set by the driver */
+};
+
+struct sh_dmae_regs {
+       u32 sar; /* SAR / source address */
+       u32 dar; /* DAR / destination address */
+       u32 tcr; /* TCR / transfer count */
+};
+
+struct sh_desc {
+       struct sh_dmae_regs hw;
+       struct list_head node;
+       struct dma_async_tx_descriptor async_tx;
+       enum dma_data_direction direction;
+       dma_cookie_t cookie;
+       size_t partial;
+       int chunks;
+       int mark;
+};
+
+#endif
index 7dab7b23a5ecff3fd1d1bedc6384ef289aa20531..f689554e17c1ff81b40f3c0bf0b998357ef65319 100644 (file)
@@ -291,21 +291,21 @@ unsigned long long poke_real_address_q(unsigned long long addr,
  * doesn't exist, so everything must go through page tables.
  */
 #ifdef CONFIG_MMU
-void __iomem *__ioremap_caller(unsigned long offset, unsigned long size,
+void __iomem *__ioremap_caller(phys_addr_t offset, unsigned long size,
                               pgprot_t prot, void *caller);
 void __iounmap(void __iomem *addr);
 
 static inline void __iomem *
-__ioremap(unsigned long offset, unsigned long size, pgprot_t prot)
+__ioremap(phys_addr_t offset, unsigned long size, pgprot_t prot)
 {
        return __ioremap_caller(offset, size, prot, __builtin_return_address(0));
 }
 
 static inline void __iomem *
-__ioremap_29bit(unsigned long offset, unsigned long size, pgprot_t prot)
+__ioremap_29bit(phys_addr_t offset, unsigned long size, pgprot_t prot)
 {
 #ifdef CONFIG_29BIT
-       unsigned long last_addr = offset + size - 1;
+       phys_addr_t last_addr = offset + size - 1;
 
        /*
         * For P1 and P2 space this is trivial, as everything is already
@@ -329,7 +329,7 @@ __ioremap_29bit(unsigned long offset, unsigned long size, pgprot_t prot)
 }
 
 static inline void __iomem *
-__ioremap_mode(unsigned long offset, unsigned long size, pgprot_t prot)
+__ioremap_mode(phys_addr_t offset, unsigned long size, pgprot_t prot)
 {
        void __iomem *ret;
 
@@ -349,35 +349,32 @@ __ioremap_mode(unsigned long offset, unsigned long size, pgprot_t prot)
 #define __iounmap(addr)                                do { } while (0)
 #endif /* CONFIG_MMU */
 
-static inline void __iomem *
-ioremap(unsigned long offset, unsigned long size)
+static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
 {
        return __ioremap_mode(offset, size, PAGE_KERNEL_NOCACHE);
 }
 
 static inline void __iomem *
-ioremap_cache(unsigned long offset, unsigned long size)
+ioremap_cache(phys_addr_t offset, unsigned long size)
 {
        return __ioremap_mode(offset, size, PAGE_KERNEL);
 }
 
 #ifdef CONFIG_HAVE_IOREMAP_PROT
 static inline void __iomem *
-ioremap_prot(resource_size_t offset, unsigned long size, unsigned long flags)
+ioremap_prot(phys_addr_t offset, unsigned long size, unsigned long flags)
 {
        return __ioremap_mode(offset, size, __pgprot(flags));
 }
 #endif
 
 #ifdef CONFIG_IOREMAP_FIXED
-extern void __iomem *ioremap_fixed(resource_size_t, unsigned long,
-                                  unsigned long, pgprot_t);
+extern void __iomem *ioremap_fixed(phys_addr_t, unsigned long, pgprot_t);
 extern int iounmap_fixed(void __iomem *);
 extern void ioremap_fixed_init(void);
 #else
 static inline void __iomem *
-ioremap_fixed(resource_size_t phys_addr, unsigned long offset,
-             unsigned long size, pgprot_t prot)
+ioremap_fixed(phys_addr_t phys_addr, unsigned long size, pgprot_t prot)
 {
        BUG();
        return NULL;
index 15a05b615ba7f60e293928723215c73e392362e8..19fe84550b4915b7e951b97d52637c86cbd77b31 100644 (file)
@@ -55,19 +55,29 @@ typedef struct {
 
 #ifdef CONFIG_PMB
 /* arch/sh/mm/pmb.c */
-long pmb_remap(unsigned long virt, unsigned long phys,
-              unsigned long size, pgprot_t prot);
-void pmb_unmap(unsigned long addr);
-void pmb_init(void);
 bool __in_29bit_mode(void);
+
+void pmb_init(void);
+int pmb_bolt_mapping(unsigned long virt, phys_addr_t phys,
+                    unsigned long size, pgprot_t prot);
+void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size,
+                              pgprot_t prot, void *caller);
+int pmb_unmap(void __iomem *addr);
+
 #else
-static inline long pmb_remap(unsigned long virt, unsigned long phys,
-                            unsigned long size, pgprot_t prot)
+
+static inline void __iomem *
+pmb_remap_caller(phys_addr_t phys, unsigned long size,
+                pgprot_t prot, void *caller)
+{
+       return NULL;
+}
+
+static inline int pmb_unmap(void __iomem *addr)
 {
        return -EINVAL;
 }
 
-#define pmb_unmap(addr)                do { } while (0)
 #define pmb_init(addr)         do { } while (0)
 
 #ifdef CONFIG_29BIT
@@ -77,6 +87,13 @@ static inline long pmb_remap(unsigned long virt, unsigned long phys,
 #endif
 
 #endif /* CONFIG_PMB */
+
+static inline void __iomem *
+pmb_remap(phys_addr_t phys, unsigned long size, pgprot_t prot)
+{
+       return pmb_remap_caller(phys, size, prot, __builtin_return_address(0));
+}
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __MMU_H */
index 57565a3b551f51ed62e672d0c1b55abb225b6684..f1b1e6944a5ff3cfb13681226da767f9f0baadb9 100644 (file)
@@ -11,7 +11,7 @@
 #ifndef ASM_SIU_H
 #define ASM_SIU_H
 
-#include <asm/dma-sh.h>
+#include <asm/dmaengine.h>
 
 struct device;
 
index 37cdadd975ac93e79d0f0fa281d9836697f88054..88e734069fa655de6022f91554173b67750b3820 100644 (file)
@@ -35,7 +35,7 @@
 
 #define pcibus_to_node(bus)    ((void)(bus), -1)
 #define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
-                                       CPU_MASK_ALL_PTR : \
+                                       cpu_all_mask : \
                                        cpumask_of_node(pcibus_to_node(bus)))
 
 #endif
diff --git a/arch/sh/include/cpu-sh3/cpu/dma-register.h b/arch/sh/include/cpu-sh3/cpu/dma-register.h
new file mode 100644 (file)
index 0000000..2349e48
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SH3 CPU-specific DMA definitions, used by both DMA drivers
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.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.
+ */
+#ifndef CPU_DMA_REGISTER_H
+#define CPU_DMA_REGISTER_H
+
+#define CHCR_TS_LOW_MASK       0x18
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+
+#define DMAOR_INIT     DMAOR_DME
+
+/*
+ * The SuperH DMAC supports a number of transmit sizes, we list them here,
+ * with their respective values as they appear in the CHCR registers.
+ */
+enum {
+       XMIT_SZ_8BIT,
+       XMIT_SZ_16BIT,
+       XMIT_SZ_32BIT,
+       XMIT_SZ_128BIT,
+};
+
+/* log2(size / 8) - used to calculate number of transfers */
+#define TS_SHIFT {                     \
+       [XMIT_SZ_8BIT]          = 0,    \
+       [XMIT_SZ_16BIT]         = 1,    \
+       [XMIT_SZ_32BIT]         = 2,    \
+       [XMIT_SZ_128BIT]        = 4,    \
+}
+
+#define TS_INDEX2VAL(i)        (((i) & 3) << CHCR_TS_LOW_SHIFT)
+
+#endif
index 207811a7a65047c494667cf32fa434344d4cfa8f..24e28b91c9d58d2b37c40ff0d1bde66f0a6aaf71 100644 (file)
 #define TS_32          0x00000010
 #define TS_128         0x00000018
 
-#define CHCR_TS_LOW_MASK       0x18
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
-
-#define DMAOR_INIT     DMAOR_DME
-
-/*
- * The SuperH DMAC supports a number of transmit sizes, we list them here,
- * with their respective values as they appear in the CHCR registers.
- */
-enum {
-       XMIT_SZ_8BIT,
-       XMIT_SZ_16BIT,
-       XMIT_SZ_32BIT,
-       XMIT_SZ_128BIT,
-};
-
-#define TS_SHIFT {                     \
-       [XMIT_SZ_8BIT]          = 0,    \
-       [XMIT_SZ_16BIT]         = 1,    \
-       [XMIT_SZ_32BIT]         = 2,    \
-       [XMIT_SZ_128BIT]        = 4,    \
-}
-
-#define TS_INDEX2VAL(i)        (((i) & 3) << CHCR_TS_LOW_SHIFT)
-
 #endif /* __ASM_CPU_SH3_DMA_H */
diff --git a/arch/sh/include/cpu-sh4/cpu/dma-register.h b/arch/sh/include/cpu-sh4/cpu/dma-register.h
new file mode 100644 (file)
index 0000000..55f9fec
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * SH4 CPU-specific DMA definitions, used by both DMA drivers
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.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.
+ */
+#ifndef CPU_DMA_REGISTER_H
+#define CPU_DMA_REGISTER_H
+
+/* SH7751/7760/7780 DMA IRQ sources */
+
+#ifdef CONFIG_CPU_SH4A
+
+#define DMAOR_INIT     DMAOR_DME
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7343) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7730)
+#define CHCR_TS_LOW_MASK       0x00000018
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+#elif defined(CONFIG_CPU_SUBTYPE_SH7722) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7724)
+#define CHCR_TS_LOW_MASK       0x00000018
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0x00300000
+#define CHCR_TS_HIGH_SHIFT     (20 - 2)        /* 2 bits for shifted low TS */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
+       defined(CONFIG_CPU_SUBTYPE_SH7764)
+#define CHCR_TS_LOW_MASK       0x00000018
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
+#define CHCR_TS_LOW_MASK       0x00000018
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+#define CHCR_TS_LOW_MASK       0x00000018
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+#else /* SH7785 */
+#define CHCR_TS_LOW_MASK       0x00000018
+#define CHCR_TS_LOW_SHIFT      3
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+#endif
+
+/* Transmit sizes and respective CHCR register values */
+enum {
+       XMIT_SZ_8BIT            = 0,
+       XMIT_SZ_16BIT           = 1,
+       XMIT_SZ_32BIT           = 2,
+       XMIT_SZ_64BIT           = 7,
+       XMIT_SZ_128BIT          = 3,
+       XMIT_SZ_256BIT          = 4,
+       XMIT_SZ_128BIT_BLK      = 0xb,
+       XMIT_SZ_256BIT_BLK      = 0xc,
+};
+
+/* log2(size / 8) - used to calculate number of transfers */
+#define TS_SHIFT {                     \
+       [XMIT_SZ_8BIT]          = 0,    \
+       [XMIT_SZ_16BIT]         = 1,    \
+       [XMIT_SZ_32BIT]         = 2,    \
+       [XMIT_SZ_64BIT]         = 3,    \
+       [XMIT_SZ_128BIT]        = 4,    \
+       [XMIT_SZ_256BIT]        = 5,    \
+       [XMIT_SZ_128BIT_BLK]    = 4,    \
+       [XMIT_SZ_256BIT_BLK]    = 5,    \
+}
+
+#define TS_INDEX2VAL(i)        ((((i) & 3) << CHCR_TS_LOW_SHIFT) | \
+                        ((((i) >> 2) & 3) << CHCR_TS_HIGH_SHIFT))
+
+#else /* CONFIG_CPU_SH4A */
+
+#define DMAOR_INIT     (0x8000 | DMAOR_DME)
+
+#define CHCR_TS_LOW_MASK       0x70
+#define CHCR_TS_LOW_SHIFT      4
+#define CHCR_TS_HIGH_MASK      0
+#define CHCR_TS_HIGH_SHIFT     0
+
+/* Transmit sizes and respective CHCR register values */
+enum {
+       XMIT_SZ_8BIT    = 1,
+       XMIT_SZ_16BIT   = 2,
+       XMIT_SZ_32BIT   = 3,
+       XMIT_SZ_64BIT   = 0,
+       XMIT_SZ_256BIT  = 4,
+};
+
+/* log2(size / 8) - used to calculate number of transfers */
+#define TS_SHIFT {                     \
+       [XMIT_SZ_8BIT]          = 0,    \
+       [XMIT_SZ_16BIT]         = 1,    \
+       [XMIT_SZ_32BIT]         = 2,    \
+       [XMIT_SZ_64BIT]         = 3,    \
+       [XMIT_SZ_256BIT]        = 5,    \
+}
+
+#define TS_INDEX2VAL(i)        (((i) & 7) << CHCR_TS_LOW_SHIFT)
+
+#endif /* CONFIG_CPU_SH4A */
+
+#endif
index e734ea47d8a09b57990f31add924a99cca373697..9647e681fd276e978a418f573fc90462a9705760 100644 (file)
@@ -8,20 +8,12 @@
 #define DMAE0_IRQ      78      /* DMA Error IRQ*/
 #define SH_DMAC_BASE0  0xFE008020
 #define SH_DMARS_BASE0 0xFE009000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
 #elif defined(CONFIG_CPU_SUBTYPE_SH7722)
 #define DMTE0_IRQ      48
 #define DMTE4_IRQ      76
 #define DMAE0_IRQ      78      /* DMA Error IRQ*/
 #define SH_DMAC_BASE0  0xFE008020
 #define SH_DMARS_BASE0 0xFE009000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0x00300000
-#define CHCR_TS_HIGH_SHIFT     20
 #elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
        defined(CONFIG_CPU_SUBTYPE_SH7764)
 #define DMTE0_IRQ      34
 #define DMAE0_IRQ      38
 #define SH_DMAC_BASE0  0xFF608020
 #define SH_DMARS_BASE0 0xFF609000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
 #elif defined(CONFIG_CPU_SUBTYPE_SH7723)
 #define DMTE0_IRQ      48      /* DMAC0A*/
 #define DMTE4_IRQ      76      /* DMAC0B */
 #define SH_DMAC_BASE0  0xFE008020
 #define SH_DMAC_BASE1  0xFDC08020
 #define SH_DMARS_BASE0 0xFDC09000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
 #elif defined(CONFIG_CPU_SUBTYPE_SH7724)
 #define DMTE0_IRQ      48      /* DMAC0A*/
 #define DMTE4_IRQ      76      /* DMAC0B */
 #define SH_DMAC_BASE1  0xFDC08020
 #define SH_DMARS_BASE0 0xFE009000
 #define SH_DMARS_BASE1 0xFDC09000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0x00600000
-#define CHCR_TS_HIGH_SHIFT     21
 #elif defined(CONFIG_CPU_SUBTYPE_SH7780)
 #define DMTE0_IRQ      34
 #define DMTE4_IRQ      44
 #define SH_DMAC_BASE0  0xFC808020
 #define SH_DMAC_BASE1  0xFC818020
 #define SH_DMARS_BASE0 0xFC809000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
 #else /* SH7785 */
 #define DMTE0_IRQ      33
 #define DMTE4_IRQ      37
 #define SH_DMAC_BASE0  0xFC808020
 #define SH_DMAC_BASE1  0xFCC08020
 #define SH_DMARS_BASE0 0xFC809000
-#define CHCR_TS_LOW_MASK       0x00000018
-#define CHCR_TS_LOW_SHIFT      3
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
 #endif
 
 #define REQ_HE         0x000000C0
 #define REQ_LE         0x00000040
 #define TM_BURST       0x00000020
 
-/*
- * The SuperH DMAC supports a number of transmit sizes, we list them here,
- * with their respective values as they appear in the CHCR registers.
- *
- * Defaults to a 64-bit transfer size.
- */
-enum {
-       XMIT_SZ_8BIT            = 0,
-       XMIT_SZ_16BIT           = 1,
-       XMIT_SZ_32BIT           = 2,
-       XMIT_SZ_64BIT           = 7,
-       XMIT_SZ_128BIT          = 3,
-       XMIT_SZ_256BIT          = 4,
-       XMIT_SZ_128BIT_BLK      = 0xb,
-       XMIT_SZ_256BIT_BLK      = 0xc,
-};
-
-/*
- * The DMA count is defined as the number of bytes to transfer.
- */
-#define TS_SHIFT {                     \
-       [XMIT_SZ_8BIT]          = 0,    \
-       [XMIT_SZ_16BIT]         = 1,    \
-       [XMIT_SZ_32BIT]         = 2,    \
-       [XMIT_SZ_64BIT]         = 3,    \
-       [XMIT_SZ_128BIT]        = 4,    \
-       [XMIT_SZ_256BIT]        = 5,    \
-       [XMIT_SZ_128BIT_BLK]    = 4,    \
-       [XMIT_SZ_256BIT_BLK]    = 5,    \
-}
-
-#define TS_INDEX2VAL(i)        ((((i) & 3) << CHCR_TS_LOW_SHIFT) | \
-                        ((((i) >> 2) & 3) << CHCR_TS_HIGH_SHIFT))
-
 #endif /* __ASM_SH_CPU_SH4_DMA_SH7780_H */
index 114a369705bcfddc30c60eb7f9981edee6f1c75b..ca747e93c2ed7d73cfc426986fbf4caa294042a7 100644 (file)
@@ -5,9 +5,8 @@
 
 #ifdef CONFIG_CPU_SH4A
 
-#define DMAOR_INIT     (DMAOR_DME)
-
 #include <cpu/dma-sh4a.h>
+
 #else /* CONFIG_CPU_SH4A */
 /*
  * SH7750/SH7751/SH7760
@@ -17,7 +16,6 @@
 #define DMTE6_IRQ      46
 #define DMAE0_IRQ      38
 
-#define DMAOR_INIT     (0x8000|DMAOR_DME)
 #define SH_DMAC_BASE0  0xffa00000
 #define SH_DMAC_BASE1  0xffa00070
 /* Definitions for the SuperH DMAC */
 #define TS_32          0x00000030
 #define TS_64          0x00000000
 
-#define CHCR_TS_LOW_MASK       0x70
-#define CHCR_TS_LOW_SHIFT      4
-#define CHCR_TS_HIGH_MASK      0
-#define CHCR_TS_HIGH_SHIFT     0
-
 #define DMAOR_COD      0x00000008
 
-/*
- * The SuperH DMAC supports a number of transmit sizes, we list them here,
- * with their respective values as they appear in the CHCR registers.
- *
- * Defaults to a 64-bit transfer size.
- */
-enum {
-       XMIT_SZ_8BIT    = 1,
-       XMIT_SZ_16BIT   = 2,
-       XMIT_SZ_32BIT   = 3,
-       XMIT_SZ_64BIT   = 0,
-       XMIT_SZ_256BIT  = 4,
-};
-
-/*
- * The DMA count is defined as the number of bytes to transfer.
- */
-#define TS_SHIFT {                     \
-       [XMIT_SZ_8BIT]          = 0,    \
-       [XMIT_SZ_16BIT]         = 1,    \
-       [XMIT_SZ_32BIT]         = 2,    \
-       [XMIT_SZ_64BIT]         = 3,    \
-       [XMIT_SZ_256BIT]        = 5,    \
-}
-
-#define TS_INDEX2VAL(i)        (((i) & 7) << CHCR_TS_LOW_SHIFT)
-
 #endif
 
 #endif /* __ASM_CPU_SH4_DMA_H */
index cee6cb88e0209eb2b13adb454e60c22bc1ed177a..42fccf93412e0996c878835c70257e90d38544f6 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __ASM_SH_MIGOR_H
 #define __ASM_SH_MIGOR_H
 
+#define PORT_MSELCRA 0xa4050180
 #define PORT_MSELCRB 0xa4050182
 #define BSC_CS4BCR 0xfec10010
 #define BSC_CS6ABCR 0xfec1001c
index ef3f97827808b1a678ee69e2a87f45aa696149ef..fd7e3639e84504501e9e090514f3876fdd97ebc3 100644 (file)
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
-#include <linux/platform_device.h>
 #include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_sci.h>
-#include <linux/mm.h>
+#include <linux/sh_timer.h>
 #include <linux/uio_driver.h>
 #include <linux/usb/m66592.h>
-#include <linux/sh_timer.h>
+
 #include <asm/clock.h>
+#include <asm/dmaengine.h>
 #include <asm/mmzone.h>
-#include <asm/dma-sh.h>
+#include <asm/siu.h>
+
+#include <cpu/dma-register.h>
 #include <cpu/sh7722.h>
 
+static struct sh_dmae_slave_config sh7722_dmae_slaves[] = {
+       {
+               .slave_id       = SHDMA_SLAVE_SCIF0_TX,
+               .addr           = 0xffe0000c,
+               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .mid_rid        = 0x21,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SCIF0_RX,
+               .addr           = 0xffe00014,
+               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .mid_rid        = 0x22,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SCIF1_TX,
+               .addr           = 0xffe1000c,
+               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .mid_rid        = 0x25,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SCIF1_RX,
+               .addr           = 0xffe10014,
+               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .mid_rid        = 0x26,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SCIF2_TX,
+               .addr           = 0xffe2000c,
+               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .mid_rid        = 0x29,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SCIF2_RX,
+               .addr           = 0xffe20014,
+               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+               .mid_rid        = 0x2a,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SIUA_TX,
+               .addr           = 0xa454c098,
+               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb1,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SIUA_RX,
+               .addr           = 0xa454c090,
+               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb2,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SIUB_TX,
+               .addr           = 0xa454c09c,
+               .chcr           = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb5,
+       }, {
+               .slave_id       = SHDMA_SLAVE_SIUB_RX,
+               .addr           = 0xa454c094,
+               .chcr           = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_32BIT),
+               .mid_rid        = 0xb6,
+       },
+};
+
+static struct sh_dmae_channel sh7722_dmae_channels[] = {
+       {
+               .offset = 0,
+               .dmars = 0,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x10,
+               .dmars = 0,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x20,
+               .dmars = 4,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x30,
+               .dmars = 4,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x50,
+               .dmars = 8,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x60,
+               .dmars = 8,
+               .dmars_bit = 8,
+       }
+};
+
+static unsigned int ts_shift[] = TS_SHIFT;
+
+static struct sh_dmae_pdata dma_platform_data = {
+       .slave          = sh7722_dmae_slaves,
+       .slave_num      = ARRAY_SIZE(sh7722_dmae_slaves),
+       .channel        = sh7722_dmae_channels,
+       .channel_num    = ARRAY_SIZE(sh7722_dmae_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
+};
+
+static struct resource sh7722_dmae_resources[] = {
+       [0] = {
+               /* Channel registers and DMAOR */
+               .start  = 0xfe008020,
+               .end    = 0xfe00808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* DMARSx */
+               .start  = 0xfe009000,
+               .end    = 0xfe00900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMA error IRQ */
+               .start  = 78,
+               .end    = 78,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-3 */
+               .start  = 48,
+               .end    = 51,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 4-5 */
+               .start  = 76,
+               .end    = 77,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+struct platform_device dma_device = {
+       .name           = "sh-dma-engine",
+       .id             = -1,
+       .resource       = sh7722_dmae_resources,
+       .num_resources  = ARRAY_SIZE(sh7722_dmae_resources),
+       .dev            = {
+               .platform_data  = &dma_platform_data,
+       },
+       .archdata = {
+               .hwblk_id = HWBLK_DMAC,
+       },
+};
+
 /* Serial */
 static struct plat_sci_port scif0_platform_data = {
        .mapbase        = 0xffe00000,
@@ -388,15 +536,36 @@ static struct platform_device tmu2_device = {
        },
 };
 
-static struct sh_dmae_pdata dma_platform_data = {
-       .mode = 0,
+static struct siu_platform siu_platform_data = {
+       .dma_dev        = &dma_device.dev,
+       .dma_slave_tx_a = SHDMA_SLAVE_SIUA_TX,
+       .dma_slave_rx_a = SHDMA_SLAVE_SIUA_RX,
+       .dma_slave_tx_b = SHDMA_SLAVE_SIUB_TX,
+       .dma_slave_rx_b = SHDMA_SLAVE_SIUB_RX,
 };
 
-static struct platform_device dma_device = {
-       .name           = "sh-dma-engine",
+static struct resource siu_resources[] = {
+       [0] = {
+               .start  = 0xa4540000,
+               .end    = 0xa454c10f,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 108,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device siu_device = {
+       .name           = "sh_siu",
        .id             = -1,
-       .dev            = {
-               .platform_data  = &dma_platform_data,
+       .dev = {
+               .platform_data  = &siu_platform_data,
+       },
+       .resource       = siu_resources,
+       .num_resources  = ARRAY_SIZE(siu_resources),
+       .archdata = {
+               .hwblk_id = HWBLK_SIU,
        },
 };
 
@@ -414,6 +583,7 @@ static struct platform_device *sh7722_devices[] __initdata = {
        &vpu_device,
        &veu_device,
        &jpu_device,
+       &siu_device,
        &dma_device,
 };
 
index 31e3451f7e3d1d1f75312baf587847926fa2aa61..e7fa2a92fc1fa1994a7102f9f4a52d7144c9c487 100644 (file)
 #include <linux/sh_timer.h>
 #include <linux/io.h>
 #include <linux/notifier.h>
+
 #include <asm/suspend.h>
 #include <asm/clock.h>
-#include <asm/dma-sh.h>
+#include <asm/dmaengine.h>
 #include <asm/mmzone.h>
+
+#include <cpu/dma-register.h>
 #include <cpu/sh7724.h>
 
 /* DMA */
-static struct sh_dmae_pdata dma_platform_data = {
-       .mode = SHDMA_DMAOR1,
+static struct sh_dmae_channel sh7724_dmae0_channels[] = {
+       {
+               .offset = 0,
+               .dmars = 0,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x10,
+               .dmars = 0,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x20,
+               .dmars = 4,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x30,
+               .dmars = 4,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x50,
+               .dmars = 8,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x60,
+               .dmars = 8,
+               .dmars_bit = 8,
+       }
+};
+
+static struct sh_dmae_channel sh7724_dmae1_channels[] = {
+       {
+               .offset = 0,
+               .dmars = 0,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x10,
+               .dmars = 0,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x20,
+               .dmars = 4,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x30,
+               .dmars = 4,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x50,
+               .dmars = 8,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x60,
+               .dmars = 8,
+               .dmars_bit = 8,
+       }
+};
+
+static unsigned int ts_shift[] = TS_SHIFT;
+
+static struct sh_dmae_pdata dma0_platform_data = {
+       .channel        = sh7724_dmae0_channels,
+       .channel_num    = ARRAY_SIZE(sh7724_dmae0_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
+};
+
+static struct sh_dmae_pdata dma1_platform_data = {
+       .channel        = sh7724_dmae1_channels,
+       .channel_num    = ARRAY_SIZE(sh7724_dmae1_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
+};
+
+/* Resource order important! */
+static struct resource sh7724_dmae0_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xfe008020,
+               .end    = 0xfe00808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMARSx */
+               .start  = 0xfe009000,
+               .end    = 0xfe00900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMA error IRQ */
+               .start  = 78,
+               .end    = 78,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-3 */
+               .start  = 48,
+               .end    = 51,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 4-5 */
+               .start  = 76,
+               .end    = 77,
+               .flags  = IORESOURCE_IRQ,
+       },
 };
 
-static struct platform_device dma_device = {
-       .name   = "sh-dma-engine",
-       .id             = -1,
-       .dev    = {
-               .platform_data  = &dma_platform_data,
+/* Resource order important! */
+static struct resource sh7724_dmae1_resources[] = {
+       {
+               /* Channel registers and DMAOR */
+               .start  = 0xfdc08020,
+               .end    = 0xfdc0808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMARSx */
+               .start  = 0xfdc09000,
+               .end    = 0xfdc0900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* DMA error IRQ */
+               .start  = 74,
+               .end    = 74,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 0-3 */
+               .start  = 40,
+               .end    = 43,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               /* IRQ for channels 4-5 */
+               .start  = 72,
+               .end    = 73,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device dma0_device = {
+       .name           = "sh-dma-engine",
+       .id             = 0,
+       .resource       = sh7724_dmae0_resources,
+       .num_resources  = ARRAY_SIZE(sh7724_dmae0_resources),
+       .dev            = {
+               .platform_data  = &dma0_platform_data,
+       },
+       .archdata = {
+               .hwblk_id = HWBLK_DMAC0,
+       },
+};
+
+static struct platform_device dma1_device = {
+       .name           = "sh-dma-engine",
+       .id             = 1,
+       .resource       = sh7724_dmae1_resources,
+       .num_resources  = ARRAY_SIZE(sh7724_dmae1_resources),
+       .dev            = {
+               .platform_data  = &dma1_platform_data,
+       },
+       .archdata = {
+               .hwblk_id = HWBLK_DMAC1,
        },
 };
 
@@ -663,7 +830,8 @@ static struct platform_device *sh7724_devices[] __initdata = {
        &tmu3_device,
        &tmu4_device,
        &tmu5_device,
-       &dma_device,
+       &dma0_device,
+       &dma1_device,
        &rtc_device,
        &iic0_device,
        &iic1_device,
index f8f21618d78597f9b3a7b1ca352abb9b6a4d0ce7..02e792c90de6b2ceece7a0c3d75415232fc08ff8 100644 (file)
 #include <linux/io.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_timer.h>
-#include <asm/dma-sh.h>
+
+#include <asm/dmaengine.h>
+
+#include <cpu/dma-register.h>
 
 static struct plat_sci_port scif0_platform_data = {
        .mapbase        = 0xffe00000,
@@ -247,15 +250,131 @@ static struct platform_device rtc_device = {
        .resource       = rtc_resources,
 };
 
-static struct sh_dmae_pdata dma_platform_data = {
-       .mode = (SHDMA_MIX_IRQ | SHDMA_DMAOR1),
+/* DMA */
+static struct sh_dmae_channel sh7780_dmae0_channels[] = {
+       {
+               .offset = 0,
+               .dmars = 0,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x10,
+               .dmars = 0,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x20,
+               .dmars = 4,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x30,
+               .dmars = 4,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x50,
+               .dmars = 8,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x60,
+               .dmars = 8,
+               .dmars_bit = 8,
+       }
+};
+
+static struct sh_dmae_channel sh7780_dmae1_channels[] = {
+       {
+               .offset = 0,
+       }, {
+               .offset = 0x10,
+       }, {
+               .offset = 0x20,
+       }, {
+               .offset = 0x30,
+       }, {
+               .offset = 0x50,
+       }, {
+               .offset = 0x60,
+       }
+};
+
+static unsigned int ts_shift[] = TS_SHIFT;
+
+static struct sh_dmae_pdata dma0_platform_data = {
+       .channel        = sh7780_dmae0_channels,
+       .channel_num    = ARRAY_SIZE(sh7780_dmae0_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
+};
+
+static struct sh_dmae_pdata dma1_platform_data = {
+       .channel        = sh7780_dmae1_channels,
+       .channel_num    = ARRAY_SIZE(sh7780_dmae1_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
 };
 
-static struct platform_device dma_device = {
+static struct resource sh7780_dmae0_resources[] = {
+       [0] = {
+               /* Channel registers and DMAOR */
+               .start  = 0xfc808020,
+               .end    = 0xfc80808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* DMARSx */
+               .start  = 0xfc809000,
+               .end    = 0xfc80900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* Real DMA error IRQ is 38, and channel IRQs are 34-37, 44-45 */
+               .start  = 34,
+               .end    = 34,
+               .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
+       },
+};
+
+static struct resource sh7780_dmae1_resources[] = {
+       [0] = {
+               /* Channel registers and DMAOR */
+               .start  = 0xfc818020,
+               .end    = 0xfc81808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       /* DMAC1 has no DMARS */
+       {
+               /* Real DMA error IRQ is 38, and channel IRQs are 46-47, 92-95 */
+               .start  = 46,
+               .end    = 46,
+               .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
+       },
+};
+
+static struct platform_device dma0_device = {
        .name           = "sh-dma-engine",
-       .id             = -1,
+       .id             = 0,
+       .resource       = sh7780_dmae0_resources,
+       .num_resources  = ARRAY_SIZE(sh7780_dmae0_resources),
        .dev            = {
-               .platform_data  = &dma_platform_data,
+               .platform_data  = &dma0_platform_data,
+       },
+};
+
+static struct platform_device dma1_device = {
+       .name           = "sh-dma-engine",
+       .id             = 1,
+       .resource       = sh7780_dmae1_resources,
+       .num_resources  = ARRAY_SIZE(sh7780_dmae1_resources),
+       .dev            = {
+               .platform_data  = &dma1_platform_data,
        },
 };
 
@@ -269,7 +388,8 @@ static struct platform_device *sh7780_devices[] __initdata = {
        &tmu4_device,
        &tmu5_device,
        &rtc_device,
-       &dma_device,
+       &dma0_device,
+       &dma1_device,
 };
 
 static int __init sh7780_devices_setup(void)
index 23448d8c6711d9c6c16f6290438afea72b76f7cf..1fcd88b1671e930bdd9e2b35f850d99336062b54 100644 (file)
 #include <linux/io.h>
 #include <linux/mm.h>
 #include <linux/sh_timer.h>
-#include <asm/dma-sh.h>
+
+#include <asm/dmaengine.h>
 #include <asm/mmzone.h>
 
+#include <cpu/dma-register.h>
+
 static struct plat_sci_port scif0_platform_data = {
        .mapbase        = 0xffea0000,
        .flags          = UPF_BOOT_AUTOCONF,
@@ -295,15 +298,131 @@ static struct platform_device tmu5_device = {
        .num_resources  = ARRAY_SIZE(tmu5_resources),
 };
 
-static struct sh_dmae_pdata dma_platform_data = {
-       .mode = (SHDMA_MIX_IRQ | SHDMA_DMAOR1),
+/* DMA */
+static struct sh_dmae_channel sh7785_dmae0_channels[] = {
+       {
+               .offset = 0,
+               .dmars = 0,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x10,
+               .dmars = 0,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x20,
+               .dmars = 4,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x30,
+               .dmars = 4,
+               .dmars_bit = 8,
+       }, {
+               .offset = 0x50,
+               .dmars = 8,
+               .dmars_bit = 0,
+       }, {
+               .offset = 0x60,
+               .dmars = 8,
+               .dmars_bit = 8,
+       }
+};
+
+static struct sh_dmae_channel sh7785_dmae1_channels[] = {
+       {
+               .offset = 0,
+       }, {
+               .offset = 0x10,
+       }, {
+               .offset = 0x20,
+       }, {
+               .offset = 0x30,
+       }, {
+               .offset = 0x50,
+       }, {
+               .offset = 0x60,
+       }
+};
+
+static unsigned int ts_shift[] = TS_SHIFT;
+
+static struct sh_dmae_pdata dma0_platform_data = {
+       .channel        = sh7785_dmae0_channels,
+       .channel_num    = ARRAY_SIZE(sh7785_dmae0_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
+};
+
+static struct sh_dmae_pdata dma1_platform_data = {
+       .channel        = sh7785_dmae1_channels,
+       .channel_num    = ARRAY_SIZE(sh7785_dmae1_channels),
+       .ts_low_shift   = CHCR_TS_LOW_SHIFT,
+       .ts_low_mask    = CHCR_TS_LOW_MASK,
+       .ts_high_shift  = CHCR_TS_HIGH_SHIFT,
+       .ts_high_mask   = CHCR_TS_HIGH_MASK,
+       .ts_shift       = ts_shift,
+       .ts_shift_num   = ARRAY_SIZE(ts_shift),
+       .dmaor_init     = DMAOR_INIT,
 };
 
-static struct platform_device dma_device = {
+static struct resource sh7785_dmae0_resources[] = {
+       [0] = {
+               /* Channel registers and DMAOR */
+               .start  = 0xfc808020,
+               .end    = 0xfc80808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* DMARSx */
+               .start  = 0xfc809000,
+               .end    = 0xfc80900b,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               /* Real DMA error IRQ is 39, and channel IRQs are 33-38 */
+               .start  = 33,
+               .end    = 33,
+               .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
+       },
+};
+
+static struct resource sh7785_dmae1_resources[] = {
+       [0] = {
+               /* Channel registers and DMAOR */
+               .start  = 0xfcc08020,
+               .end    = 0xfcc0808f,
+               .flags  = IORESOURCE_MEM,
+       },
+       /* DMAC1 has no DMARS */
+       {
+               /* Real DMA error IRQ is 58, and channel IRQs are 52-57 */
+               .start  = 52,
+               .end    = 52,
+               .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE,
+       },
+};
+
+static struct platform_device dma0_device = {
        .name           = "sh-dma-engine",
-       .id             = -1,
+       .id             = 0,
+       .resource       = sh7785_dmae0_resources,
+       .num_resources  = ARRAY_SIZE(sh7785_dmae0_resources),
        .dev            = {
-               .platform_data  = &dma_platform_data,
+               .platform_data  = &dma0_platform_data,
+       },
+};
+
+static struct platform_device dma1_device = {
+       .name           = "sh-dma-engine",
+       .id             = 1,
+       .resource       = sh7785_dmae1_resources,
+       .num_resources  = ARRAY_SIZE(sh7785_dmae1_resources),
+       .dev            = {
+               .platform_data  = &dma1_platform_data,
        },
 };
 
@@ -320,7 +439,8 @@ static struct platform_device *sh7785_devices[] __initdata = {
        &tmu3_device,
        &tmu4_device,
        &tmu5_device,
-       &dma_device,
+       &dma0_device,
+       &dma1_device,
 };
 
 static int __init sh7785_devices_setup(void)
index e2f1753d275c227868cae286903a4d18b6c64ae6..675eea7785d9c8a91fa9db53950e37f30e756765 100644 (file)
@@ -143,26 +143,6 @@ static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len)
        return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
 }
 
-/*
- * Store a breakpoint's encoded address, length, and type.
- */
-static int arch_store_info(struct perf_event *bp)
-{
-       struct arch_hw_breakpoint *info = counter_arch_bp(bp);
-
-       /*
-        * User-space requests will always have the address field populated
-        * For kernel-addresses, either the address or symbol name can be
-        * specified.
-        */
-       if (info->name)
-               info->address = (unsigned long)kallsyms_lookup_name(info->name);
-       if (info->address)
-               return 0;
-
-       return -EINVAL;
-}
-
 int arch_bp_generic_fields(int sh_len, int sh_type,
                           int *gen_len, int *gen_type)
 {
@@ -276,10 +256,12 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp,
                return ret;
        }
 
-       ret = arch_store_info(bp);
-
-       if (ret < 0)
-               return ret;
+       /*
+        * For kernel-addresses, either the address or symbol name can be
+        * specified.
+        */
+       if (info->name)
+               info->address = (unsigned long)kallsyms_lookup_name(info->name);
 
        /*
         * Check that the low-order bits of the address are appropriate
index 3459e70eed722c51859e01ee53fcc3bbab46dcdd..8870d6ba64bfaf3fac5b8d003a050475ef7dd25e 100644 (file)
@@ -443,7 +443,7 @@ void __init setup_arch(char **cmdline_p)
 
        nodes_clear(node_online_map);
 
-       /* Setup bootmem with available RAM */
+       pmb_init();
        lmb_init();
        setup_memory();
        sparse_init();
@@ -452,7 +452,6 @@ void __init setup_arch(char **cmdline_p)
        conswitchp = &dummy_con;
 #endif
        paging_init();
-       pmb_init();
 
        ioremap_fixed_init();
 
index 953fa1613312b313be2bb22e05031e3994922106..8a0072de2bcce23057c1f012a9fb078901c8a590 100644 (file)
@@ -39,12 +39,12 @@ static int null_rtc_set_time(const time_t secs)
 void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time;
 int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time;
 
-#ifdef CONFIG_GENERIC_CMOS_UPDATE
 void read_persistent_clock(struct timespec *ts)
 {
        rtc_sh_get_time(ts);
 }
 
+#ifdef CONFIG_GENERIC_CMOS_UPDATE
 int update_persistent_clock(struct timespec now)
 {
        return rtc_sh_set_time(now.tv_sec);
@@ -113,9 +113,5 @@ void __init time_init(void)
        hwblk_init();
        clk_init();
 
-       rtc_sh_get_time(&xtime);
-       set_normalized_timespec(&wall_to_monotonic,
-                               -xtime.tv_sec, -xtime.tv_nsec);
-
        late_time_init = sh_late_time_init;
 }
index 3f19d1c5d942207525766dae07f95139bb35dc59..05909d58e2fe1c278e3f30a3d196e1c214724194 100644 (file)
@@ -17,8 +17,7 @@ struct DWstruct {
 #error I feel sick.
 #endif
 
-typedef union
-{
+typedef union {
        struct DWstruct s;
        long long ll;
 } DWunion;
index c68d2d7d00a96343e7ab9efee4c7287e02af03f4..1ab2385ecefee5bd195e26949d62eb30ca51ccd1 100644 (file)
  * caller shouldn't need to know that small detail.
  */
 void __iomem * __init_refok
-__ioremap_caller(unsigned long phys_addr, unsigned long size,
+__ioremap_caller(phys_addr_t phys_addr, unsigned long size,
                 pgprot_t pgprot, void *caller)
 {
        struct vm_struct *area;
        unsigned long offset, last_addr, addr, orig_addr;
+       void __iomem *mapped;
 
        /* Don't allow wraparound or zero size */
        last_addr = phys_addr + size - 1;
        if (!size || last_addr < phys_addr)
                return NULL;
 
+       /*
+        * If we can't yet use the regular approach, go the fixmap route.
+        */
+       if (!mem_init_done)
+               return ioremap_fixed(phys_addr, size, pgprot);
+
+       /*
+        * First try to remap through the PMB.
+        * PMB entries are all pre-faulted.
+        */
+       mapped = pmb_remap_caller(phys_addr, size, pgprot, caller);
+       if (mapped && !IS_ERR(mapped))
+               return mapped;
+
        /*
         * Mappings have to be page-aligned
         */
@@ -52,12 +67,6 @@ __ioremap_caller(unsigned long phys_addr, unsigned long size,
        phys_addr &= PAGE_MASK;
        size = PAGE_ALIGN(last_addr+1) - phys_addr;
 
-       /*
-        * If we can't yet use the regular approach, go the fixmap route.
-        */
-       if (!mem_init_done)
-               return ioremap_fixed(phys_addr, offset, size, pgprot);
-
        /*
         * Ok, go for it..
         */
@@ -67,33 +76,10 @@ __ioremap_caller(unsigned long phys_addr, unsigned long size,
        area->phys_addr = phys_addr;
        orig_addr = addr = (unsigned long)area->addr;
 
-#ifdef CONFIG_PMB
-       /*
-        * First try to remap through the PMB once a valid VMA has been
-        * established. Smaller allocations (or the rest of the size
-        * remaining after a PMB mapping due to the size not being
-        * perfectly aligned on a PMB size boundary) are then mapped
-        * through the UTLB using conventional page tables.
-        *
-        * PMB entries are all pre-faulted.
-        */
-       if (unlikely(phys_addr >= P1SEG)) {
-               unsigned long mapped;
-
-               mapped = pmb_remap(addr, phys_addr, size, pgprot);
-               if (likely(mapped)) {
-                       addr            += mapped;
-                       phys_addr       += mapped;
-                       size            -= mapped;
-               }
+       if (ioremap_page_range(addr, addr + size, phys_addr, pgprot)) {
+               vunmap((void *)orig_addr);
+               return NULL;
        }
-#endif
-
-       if (likely(size))
-               if (ioremap_page_range(addr, addr + size, phys_addr, pgprot)) {
-                       vunmap((void *)orig_addr);
-                       return NULL;
-               }
 
        return (void __iomem *)(offset + (char *)orig_addr);
 }
@@ -133,23 +119,11 @@ void __iounmap(void __iomem *addr)
        if (iounmap_fixed(addr) == 0)
                return;
 
-#ifdef CONFIG_PMB
        /*
-        * Purge any PMB entries that may have been established for this
-        * mapping, then proceed with conventional VMA teardown.
-        *
-        * XXX: Note that due to the way that remove_vm_area() does
-        * matching of the resultant VMA, we aren't able to fast-forward
-        * the address past the PMB space until the end of the VMA where
-        * the page tables reside. As such, unmap_vm_area() will be
-        * forced to linearly scan over the area until it finds the page
-        * tables where PTEs that need to be unmapped actually reside,
-        * which is far from optimal. Perhaps we need to use a separate
-        * VMA for the PMB mappings?
-        *                                      -- PFM.
+        * If the PMB handled it, there's nothing else to do.
         */
-       pmb_unmap(vaddr);
-#endif
+       if (pmb_unmap(addr) == 0)
+               return;
 
        p = remove_vm_area((void *)(vaddr & PAGE_MASK));
        if (!p) {
index 0b78b1e20ef1d259473673de4579243f9fce16bf..7f682e5dafcf2a1465cd1578dfdd2ce803e46fc2 100644 (file)
@@ -45,14 +45,21 @@ void __init ioremap_fixed_init(void)
 }
 
 void __init __iomem *
-ioremap_fixed(resource_size_t phys_addr, unsigned long offset,
-             unsigned long size, pgprot_t prot)
+ioremap_fixed(phys_addr_t phys_addr, unsigned long size, pgprot_t prot)
 {
        enum fixed_addresses idx0, idx;
        struct ioremap_map *map;
        unsigned int nrpages;
+       unsigned long offset;
        int i, slot;
 
+       /*
+        * Mappings have to be page-aligned
+        */
+       offset = phys_addr & ~PAGE_MASK;
+       phys_addr &= PAGE_MASK;
+       size = PAGE_ALIGN(phys_addr + size) - phys_addr;
+
        slot = -1;
        for (i = 0; i < FIX_N_IOREMAPS; i++) {
                map = &ioremap_maps[i];
index 422e9272187860b24842d938a505e6585056fed9..961b34085e3b7218cd323c4545e2da52c0dc69c4 100644 (file)
@@ -74,6 +74,9 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end)
        start_pfn = start >> PAGE_SHIFT;
        end_pfn = end >> PAGE_SHIFT;
 
+       pmb_bolt_mapping((unsigned long)__va(start), start, end - start,
+                        PAGE_KERNEL);
+
        lmb_add(start, end - start);
 
        __add_active_range(nid, start_pfn, end_pfn);
index 198bcff5e96f864a39e4dbd7c8c5c0980f7d697a..a4662e2782c3fd84c8743dfb56394fdce971fe95 100644 (file)
@@ -23,7 +23,8 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/spinlock.h>
-#include <linux/rwlock.h>
+#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
 #include <asm/sizes.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -52,12 +53,24 @@ struct pmb_entry {
        struct pmb_entry *link;
 };
 
+static struct {
+       unsigned long size;
+       int flag;
+} pmb_sizes[] = {
+       { .size = SZ_512M, .flag = PMB_SZ_512M, },
+       { .size = SZ_128M, .flag = PMB_SZ_128M, },
+       { .size = SZ_64M,  .flag = PMB_SZ_64M,  },
+       { .size = SZ_16M,  .flag = PMB_SZ_16M,  },
+};
+
 static void pmb_unmap_entry(struct pmb_entry *, int depth);
 
 static DEFINE_RWLOCK(pmb_rwlock);
 static struct pmb_entry pmb_entry_list[NR_PMB_ENTRIES];
 static DECLARE_BITMAP(pmb_map, NR_PMB_ENTRIES);
 
+static unsigned int pmb_iomapping_enabled;
+
 static __always_inline unsigned long mk_pmb_entry(unsigned int entry)
 {
        return (entry & PMB_E_MASK) << PMB_E_SHIFT;
@@ -73,6 +86,142 @@ static __always_inline unsigned long mk_pmb_data(unsigned int entry)
        return mk_pmb_entry(entry) | PMB_DATA;
 }
 
+static __always_inline unsigned int pmb_ppn_in_range(unsigned long ppn)
+{
+       return ppn >= __pa(memory_start) && ppn < __pa(memory_end);
+}
+
+/*
+ * Ensure that the PMB entries match our cache configuration.
+ *
+ * When we are in 32-bit address extended mode, CCR.CB becomes
+ * invalid, so care must be taken to manually adjust cacheable
+ * translations.
+ */
+static __always_inline unsigned long pmb_cache_flags(void)
+{
+       unsigned long flags = 0;
+
+#if defined(CONFIG_CACHE_OFF)
+       flags |= PMB_WT | PMB_UB;
+#elif defined(CONFIG_CACHE_WRITETHROUGH)
+       flags |= PMB_C | PMB_WT | PMB_UB;
+#elif defined(CONFIG_CACHE_WRITEBACK)
+       flags |= PMB_C;
+#endif
+
+       return flags;
+}
+
+/*
+ * Convert typical pgprot value to the PMB equivalent
+ */
+static inline unsigned long pgprot_to_pmb_flags(pgprot_t prot)
+{
+       unsigned long pmb_flags = 0;
+       u64 flags = pgprot_val(prot);
+
+       if (flags & _PAGE_CACHABLE)
+               pmb_flags |= PMB_C;
+       if (flags & _PAGE_WT)
+               pmb_flags |= PMB_WT | PMB_UB;
+
+       return pmb_flags;
+}
+
+static inline bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b)
+{
+       return (b->vpn == (a->vpn + a->size)) &&
+              (b->ppn == (a->ppn + a->size)) &&
+              (b->flags == a->flags);
+}
+
+static bool pmb_mapping_exists(unsigned long vaddr, phys_addr_t phys,
+                              unsigned long size)
+{
+       int i;
+
+       read_lock(&pmb_rwlock);
+
+       for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) {
+               struct pmb_entry *pmbe, *iter;
+               unsigned long span;
+
+               if (!test_bit(i, pmb_map))
+                       continue;
+
+               pmbe = &pmb_entry_list[i];
+
+               /*
+                * See if VPN and PPN are bounded by an existing mapping.
+                */
+               if ((vaddr < pmbe->vpn) || (vaddr >= (pmbe->vpn + pmbe->size)))
+                       continue;
+               if ((phys < pmbe->ppn) || (phys >= (pmbe->ppn + pmbe->size)))
+                       continue;
+
+               /*
+                * Now see if we're in range of a simple mapping.
+                */
+               if (size <= pmbe->size) {
+                       read_unlock(&pmb_rwlock);
+                       return true;
+               }
+
+               span = pmbe->size;
+
+               /*
+                * Finally for sizes that involve compound mappings, walk
+                * the chain.
+                */
+               for (iter = pmbe->link; iter; iter = iter->link)
+                       span += iter->size;
+
+               /*
+                * Nothing else to do if the range requirements are met.
+                */
+               if (size <= span) {
+                       read_unlock(&pmb_rwlock);
+                       return true;
+               }
+       }
+
+       read_unlock(&pmb_rwlock);
+       return false;
+}
+
+static bool pmb_size_valid(unsigned long size)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++)
+               if (pmb_sizes[i].size == size)
+                       return true;
+
+       return false;
+}
+
+static inline bool pmb_addr_valid(unsigned long addr, unsigned long size)
+{
+       return (addr >= P1SEG && (addr + size - 1) < P3SEG);
+}
+
+static inline bool pmb_prot_valid(pgprot_t prot)
+{
+       return (pgprot_val(prot) & _PAGE_USER) == 0;
+}
+
+static int pmb_size_to_flags(unsigned long size)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++)
+               if (pmb_sizes[i].size == size)
+                       return pmb_sizes[i].flag;
+
+       return 0;
+}
+
 static int pmb_alloc_entry(void)
 {
        int pos;
@@ -140,33 +289,22 @@ static void pmb_free(struct pmb_entry *pmbe)
 }
 
 /*
- * Ensure that the PMB entries match our cache configuration.
- *
- * When we are in 32-bit address extended mode, CCR.CB becomes
- * invalid, so care must be taken to manually adjust cacheable
- * translations.
+ * Must be run uncached.
  */
-static __always_inline unsigned long pmb_cache_flags(void)
+static void __set_pmb_entry(struct pmb_entry *pmbe)
 {
-       unsigned long flags = 0;
+       unsigned long addr, data;
 
-#if defined(CONFIG_CACHE_WRITETHROUGH)
-       flags |= PMB_C | PMB_WT | PMB_UB;
-#elif defined(CONFIG_CACHE_WRITEBACK)
-       flags |= PMB_C;
-#endif
+       addr = mk_pmb_addr(pmbe->entry);
+       data = mk_pmb_data(pmbe->entry);
 
-       return flags;
-}
+       jump_to_uncached();
 
-/*
- * Must be run uncached.
- */
-static void __set_pmb_entry(struct pmb_entry *pmbe)
-{
-       writel_uncached(pmbe->vpn | PMB_V, mk_pmb_addr(pmbe->entry));
-       writel_uncached(pmbe->ppn | pmbe->flags | PMB_V,
-                       mk_pmb_data(pmbe->entry));
+       /* Set V-bit */
+       __raw_writel(pmbe->vpn | PMB_V, addr);
+       __raw_writel(pmbe->ppn | pmbe->flags | PMB_V, data);
+
+       back_to_cached();
 }
 
 static void __clear_pmb_entry(struct pmb_entry *pmbe)
@@ -194,144 +332,155 @@ static void set_pmb_entry(struct pmb_entry *pmbe)
        spin_unlock_irqrestore(&pmbe->lock, flags);
 }
 
-static struct {
-       unsigned long size;
-       int flag;
-} pmb_sizes[] = {
-       { .size = SZ_512M, .flag = PMB_SZ_512M, },
-       { .size = SZ_128M, .flag = PMB_SZ_128M, },
-       { .size = SZ_64M,  .flag = PMB_SZ_64M,  },
-       { .size = SZ_16M,  .flag = PMB_SZ_16M,  },
-};
-
-long pmb_remap(unsigned long vaddr, unsigned long phys,
-              unsigned long size, pgprot_t prot)
+int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys,
+                    unsigned long size, pgprot_t prot)
 {
        struct pmb_entry *pmbp, *pmbe;
-       unsigned long wanted;
-       int pmb_flags, i;
-       long err;
-       u64 flags;
+       unsigned long orig_addr, orig_size;
+       unsigned long flags, pmb_flags;
+       int i, mapped;
 
-       flags = pgprot_val(prot);
+       if (!pmb_addr_valid(vaddr, size))
+               return -EFAULT;
+       if (pmb_mapping_exists(vaddr, phys, size))
+               return 0;
 
-       pmb_flags = PMB_WT | PMB_UB;
-
-       /* Convert typical pgprot value to the PMB equivalent */
-       if (flags & _PAGE_CACHABLE) {
-               pmb_flags |= PMB_C;
+       orig_addr = vaddr;
+       orig_size = size;
 
-               if ((flags & _PAGE_WT) == 0)
-                       pmb_flags &= ~(PMB_WT | PMB_UB);
-       }
+       flush_tlb_kernel_range(vaddr, vaddr + size);
 
+       pmb_flags = pgprot_to_pmb_flags(prot);
        pmbp = NULL;
-       wanted = size;
 
-again:
-       for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) {
-               unsigned long flags;
+       do {
+               for (i = mapped = 0; i < ARRAY_SIZE(pmb_sizes); i++) {
+                       if (size < pmb_sizes[i].size)
+                               continue;
+
+                       pmbe = pmb_alloc(vaddr, phys, pmb_flags |
+                                        pmb_sizes[i].flag, PMB_NO_ENTRY);
+                       if (IS_ERR(pmbe)) {
+                               pmb_unmap_entry(pmbp, mapped);
+                               return PTR_ERR(pmbe);
+                       }
 
-               if (size < pmb_sizes[i].size)
-                       continue;
+                       spin_lock_irqsave(&pmbe->lock, flags);
 
-               pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag,
-                                PMB_NO_ENTRY);
-               if (IS_ERR(pmbe)) {
-                       err = PTR_ERR(pmbe);
-                       goto out;
-               }
+                       pmbe->size = pmb_sizes[i].size;
 
-               spin_lock_irqsave(&pmbe->lock, flags);
+                       __set_pmb_entry(pmbe);
 
-               __set_pmb_entry(pmbe);
+                       phys    += pmbe->size;
+                       vaddr   += pmbe->size;
+                       size    -= pmbe->size;
 
-               phys    += pmb_sizes[i].size;
-               vaddr   += pmb_sizes[i].size;
-               size    -= pmb_sizes[i].size;
+                       /*
+                        * Link adjacent entries that span multiple PMB
+                        * entries for easier tear-down.
+                        */
+                       if (likely(pmbp)) {
+                               spin_lock(&pmbp->lock);
+                               pmbp->link = pmbe;
+                               spin_unlock(&pmbp->lock);
+                       }
 
-               pmbe->size = pmb_sizes[i].size;
+                       pmbp = pmbe;
 
-               /*
-                * Link adjacent entries that span multiple PMB entries
-                * for easier tear-down.
-                */
-               if (likely(pmbp)) {
-                       spin_lock(&pmbp->lock);
-                       pmbp->link = pmbe;
-                       spin_unlock(&pmbp->lock);
+                       /*
+                        * Instead of trying smaller sizes on every
+                        * iteration (even if we succeed in allocating
+                        * space), try using pmb_sizes[i].size again.
+                        */
+                       i--;
+                       mapped++;
+
+                       spin_unlock_irqrestore(&pmbe->lock, flags);
                }
+       } while (size >= SZ_16M);
 
-               pmbp = pmbe;
+       flush_cache_vmap(orig_addr, orig_addr + orig_size);
 
-               /*
-                * Instead of trying smaller sizes on every iteration
-                * (even if we succeed in allocating space), try using
-                * pmb_sizes[i].size again.
-                */
-               i--;
+       return 0;
+}
 
-               spin_unlock_irqrestore(&pmbe->lock, flags);
-       }
+void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size,
+                              pgprot_t prot, void *caller)
+{
+       unsigned long vaddr;
+       phys_addr_t offset, last_addr;
+       phys_addr_t align_mask;
+       unsigned long aligned;
+       struct vm_struct *area;
+       int i, ret;
 
-       if (size >= SZ_16M)
-               goto again;
+       if (!pmb_iomapping_enabled)
+               return NULL;
 
-       return wanted - size;
+       /*
+        * Small mappings need to go through the TLB.
+        */
+       if (size < SZ_16M)
+               return ERR_PTR(-EINVAL);
+       if (!pmb_prot_valid(prot))
+               return ERR_PTR(-EINVAL);
 
-out:
-       pmb_unmap_entry(pmbp, NR_PMB_ENTRIES);
+       for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++)
+               if (size >= pmb_sizes[i].size)
+                       break;
+
+       last_addr = phys + size;
+       align_mask = ~(pmb_sizes[i].size - 1);
+       offset = phys & ~align_mask;
+       phys &= align_mask;
+       aligned = ALIGN(last_addr, pmb_sizes[i].size) - phys;
+
+       /*
+        * XXX: This should really start from uncached_end, but this
+        * causes the MMU to reset, so for now we restrict it to the
+        * 0xb000...0xc000 range.
+        */
+       area = __get_vm_area_caller(aligned, VM_IOREMAP, 0xb0000000,
+                                   P3SEG, caller);
+       if (!area)
+               return NULL;
+
+       area->phys_addr = phys;
+       vaddr = (unsigned long)area->addr;
+
+       ret = pmb_bolt_mapping(vaddr, phys, size, prot);
+       if (unlikely(ret != 0))
+               return ERR_PTR(ret);
 
-       return err;
+       return (void __iomem *)(offset + (char *)vaddr);
 }
 
-void pmb_unmap(unsigned long addr)
+int pmb_unmap(void __iomem *addr)
 {
        struct pmb_entry *pmbe = NULL;
-       int i;
+       unsigned long vaddr = (unsigned long __force)addr;
+       int i, found = 0;
 
        read_lock(&pmb_rwlock);
 
        for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) {
                if (test_bit(i, pmb_map)) {
                        pmbe = &pmb_entry_list[i];
-                       if (pmbe->vpn == addr)
+                       if (pmbe->vpn == vaddr) {
+                               found = 1;
                                break;
+                       }
                }
        }
 
        read_unlock(&pmb_rwlock);
 
-       pmb_unmap_entry(pmbe, NR_PMB_ENTRIES);
-}
-
-static bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b)
-{
-       return (b->vpn == (a->vpn + a->size)) &&
-              (b->ppn == (a->ppn + a->size)) &&
-              (b->flags == a->flags);
-}
-
-static bool pmb_size_valid(unsigned long size)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++)
-               if (pmb_sizes[i].size == size)
-                       return true;
-
-       return false;
-}
-
-static int pmb_size_to_flags(unsigned long size)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++)
-               if (pmb_sizes[i].size == size)
-                       return pmb_sizes[i].flag;
+       if (found) {
+               pmb_unmap_entry(pmbe, NR_PMB_ENTRIES);
+               return 0;
+       }
 
-       return 0;
+       return -EINVAL;
 }
 
 static void __pmb_unmap_entry(struct pmb_entry *pmbe, int depth)
@@ -351,6 +500,8 @@ static void __pmb_unmap_entry(struct pmb_entry *pmbe, int depth)
                 */
                __clear_pmb_entry(pmbe);
 
+               flush_cache_vunmap(pmbe->vpn, pmbe->vpn + pmbe->size);
+
                pmbe = pmblink->link;
 
                pmb_free(pmblink);
@@ -369,11 +520,6 @@ static void pmb_unmap_entry(struct pmb_entry *pmbe, int depth)
        write_unlock_irqrestore(&pmb_rwlock, flags);
 }
 
-static __always_inline unsigned int pmb_ppn_in_range(unsigned long ppn)
-{
-       return ppn >= __pa(memory_start) && ppn < __pa(memory_end);
-}
-
 static void __init pmb_notify(void)
 {
        int i;
@@ -625,6 +771,18 @@ static void __init pmb_resize(void)
 }
 #endif
 
+static int __init early_pmb(char *p)
+{
+       if (!p)
+               return 0;
+
+       if (strstr(p, "iomap"))
+               pmb_iomapping_enabled = 1;
+
+       return 0;
+}
+early_param("pmb", early_pmb);
+
 void __init pmb_init(void)
 {
        /* Synchronize software state */
@@ -713,7 +871,7 @@ static int __init pmb_debugfs_init(void)
 
        return 0;
 }
-postcore_initcall(pmb_debugfs_init);
+subsys_initcall(pmb_debugfs_init);
 
 #ifdef CONFIG_PM
 static int pmb_sysdev_suspend(struct sys_device *dev, pm_message_t state)
index 99a1f191497be29ce8e1cae10b5db72272551214..6a8d078070cafae20fbc1615da3dc014114e2528 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.33-rc2
-# Mon Jan 11 23:20:31 2010
+# Linux kernel version: 2.6.33
+# Wed Mar  3 02:52:23 2010
 #
 # CONFIG_64BIT is not set
 CONFIG_SPARC=y
@@ -9,6 +9,8 @@ CONFIG_SPARC32=y
 # CONFIG_SPARC64 is not set
 CONFIG_ARCH_DEFCONFIG="arch/sparc/configs/sparc32_defconfig"
 CONFIG_BITS=32
+CONFIG_GENERIC_TIME=y
+CONFIG_ARCH_USES_GETTIMEOFFSET=y
 CONFIG_AUDIT_ARCH=y
 CONFIG_MMU=y
 CONFIG_HIGHMEM=y
@@ -48,11 +50,6 @@ CONFIG_RCU_FANOUT=32
 # CONFIG_TREE_RCU_TRACE is not set
 # CONFIG_IKCONFIG is not set
 CONFIG_LOG_BUF_SHIFT=14
-CONFIG_GROUP_SCHED=y
-CONFIG_FAIR_GROUP_SCHED=y
-CONFIG_RT_GROUP_SCHED=y
-CONFIG_USER_SCHED=y
-# CONFIG_CGROUP_SCHED is not set
 # CONFIG_CGROUPS is not set
 CONFIG_SYSFS_DEPRECATED=y
 CONFIG_SYSFS_DEPRECATED_V2=y
@@ -68,6 +65,7 @@ CONFIG_INITRAMFS_SOURCE=""
 CONFIG_RD_GZIP=y
 CONFIG_RD_BZIP2=y
 CONFIG_RD_LZMA=y
+CONFIG_RD_LZO=y
 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_SYSCTL=y
 CONFIG_ANON_INODES=y
@@ -211,7 +209,6 @@ CONFIG_SBUSCHAR=y
 CONFIG_PCI=y
 CONFIG_PCI_SYSCALL=y
 # CONFIG_ARCH_SUPPORTS_MSI is not set
-CONFIG_PCI_LEGACY=y
 # CONFIG_PCI_DEBUG is not set
 # CONFIG_PCI_STUB is not set
 # CONFIG_PCI_IOV is not set
@@ -232,7 +229,6 @@ CONFIG_NET=y
 # Networking options
 #
 CONFIG_PACKET=y
-# CONFIG_PACKET_MMAP is not set
 CONFIG_UNIX=y
 CONFIG_XFRM=y
 CONFIG_XFRM_USER=m
@@ -379,11 +375,13 @@ CONFIG_MISC_DEVICES=y
 # CONFIG_TIFM_CORE is not set
 # CONFIG_ENCLOSURE_SERVICES is not set
 # CONFIG_HP_ILO is not set
+# CONFIG_TI_DAC7512 is not set
 # CONFIG_C2PORT is not set
 
 #
 # EEPROM support
 #
+# CONFIG_EEPROM_AT25 is not set
 # CONFIG_EEPROM_93CX6 is not set
 # CONFIG_CB710_CORE is not set
 CONFIG_HAVE_IDE=y
@@ -507,7 +505,9 @@ CONFIG_SUNQE=m
 # CONFIG_SUNGEM is not set
 # CONFIG_CASSINI is not set
 # CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_ENC28J60 is not set
 # CONFIG_ETHOC is not set
+# CONFIG_GRETH is not set
 # CONFIG_DNET is not set
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
@@ -521,6 +521,7 @@ CONFIG_SUNQE=m
 # CONFIG_NET_PCI is not set
 # CONFIG_B44 is not set
 # CONFIG_KS8842 is not set
+# CONFIG_KS8851 is not set
 # CONFIG_KS8851_MLL is not set
 # CONFIG_ATL2 is not set
 CONFIG_NETDEV_1000=y
@@ -563,6 +564,7 @@ CONFIG_CHELSIO_T3_DEPENDS=y
 # CONFIG_MLX4_CORE is not set
 # CONFIG_TEHUTI is not set
 # CONFIG_BNX2X is not set
+# CONFIG_QLCNIC is not set
 # CONFIG_QLGE is not set
 # CONFIG_SFC is not set
 # CONFIG_BE2NET is not set
@@ -665,6 +667,7 @@ CONFIG_DEVKMEM=y
 #
 # Non-8250 serial port support
 #
+# CONFIG_SERIAL_MAX3100 is not set
 CONFIG_SERIAL_SUNCORE=y
 CONFIG_SERIAL_SUNZILOG=y
 CONFIG_SERIAL_SUNZILOG_CONSOLE=y
@@ -689,7 +692,23 @@ CONFIG_HW_RANDOM=m
 # CONFIG_TCG_TPM is not set
 CONFIG_DEVPORT=y
 # CONFIG_I2C is not set
-# CONFIG_SPI is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=m
+CONFIG_SPI_XILINX=m
+CONFIG_SPI_XILINX_PLTFM=m
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
 
 #
 # PPS support
@@ -706,10 +725,13 @@ CONFIG_HWMON=y
 #
 # Native drivers
 #
+# CONFIG_SENSORS_ADCXX is not set
 # CONFIG_SENSORS_I5K_AMB is not set
 # CONFIG_SENSORS_F71805F is not set
 # CONFIG_SENSORS_F71882FG is not set
 # CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_MAX1111 is not set
 # CONFIG_SENSORS_PC87360 is not set
 # CONFIG_SENSORS_PC87427 is not set
 # CONFIG_SENSORS_SIS5595 is not set
@@ -720,6 +742,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_VT8231 is not set
 # CONFIG_SENSORS_W83627HF is not set
 # CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
 # CONFIG_THERMAL is not set
 # CONFIG_WATCHDOG is not set
 CONFIG_SSB_POSSIBLE=y
@@ -736,6 +759,8 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_SM501 is not set
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_MC13783 is not set
+# CONFIG_AB4500_CORE is not set
 # CONFIG_REGULATOR is not set
 # CONFIG_MEDIA_SUPPORT is not set
 
@@ -743,6 +768,7 @@ CONFIG_SSB_POSSIBLE=y
 # Graphics support
 #
 CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
 # CONFIG_VGASTATE is not set
 # CONFIG_VIDEO_OUTPUT_CONTROL is not set
 # CONFIG_FB is not set
@@ -808,6 +834,14 @@ CONFIG_RTC_INTF_DEV=y
 #
 # SPI RTC drivers
 #
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
 
 #
 # Platform RTC drivers
@@ -1180,9 +1214,11 @@ CONFIG_CRC32=y
 CONFIG_LIBCRC32C=m
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_DECOMPRESS=y
 CONFIG_DECOMPRESS_GZIP=y
 CONFIG_DECOMPRESS_BZIP2=y
 CONFIG_DECOMPRESS_LZMA=y
+CONFIG_DECOMPRESS_LZO=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 41c5a56aa6f28b5a20c6c9e25215dafde9b3099b..56e3163673e304d333cb856538f1e5544b64d4c7 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.33-rc2
-# Wed Jan 20 16:31:47 2010
+# Linux kernel version: 2.6.33
+# Wed Mar  3 02:54:29 2010
 #
 CONFIG_64BIT=y
 CONFIG_SPARC=y
@@ -55,14 +55,10 @@ CONFIG_TREE_RCU=y
 # CONFIG_RCU_TRACE is not set
 CONFIG_RCU_FANOUT=64
 # CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_RCU_FAST_NO_HZ is not set
 # CONFIG_TREE_RCU_TRACE is not set
 # CONFIG_IKCONFIG is not set
 CONFIG_LOG_BUF_SHIFT=18
-CONFIG_GROUP_SCHED=y
-CONFIG_FAIR_GROUP_SCHED=y
-CONFIG_RT_GROUP_SCHED=y
-CONFIG_USER_SCHED=y
-# CONFIG_CGROUP_SCHED is not set
 # CONFIG_CGROUPS is not set
 # CONFIG_SYSFS_DEPRECATED_V2 is not set
 CONFIG_RELAY=y
@@ -77,6 +73,7 @@ CONFIG_INITRAMFS_SOURCE=""
 CONFIG_RD_GZIP=y
 CONFIG_RD_BZIP2=y
 CONFIG_RD_LZMA=y
+CONFIG_RD_LZO=y
 CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_SYSCTL=y
 CONFIG_ANON_INODES=y
@@ -105,7 +102,6 @@ CONFIG_PERF_USE_VMALLOC=y
 # Kernel Performance Events And Counters
 #
 CONFIG_PERF_EVENTS=y
-CONFIG_EVENT_PROFILE=y
 CONFIG_PERF_COUNTERS=y
 # CONFIG_DEBUG_PERF_USE_VMALLOC is not set
 CONFIG_VM_EVENT_COUNTERS=y
@@ -266,7 +262,6 @@ CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_SYSCALL=y
 CONFIG_ARCH_SUPPORTS_MSI=y
 CONFIG_PCI_MSI=y
-# CONFIG_PCI_LEGACY is not set
 # CONFIG_PCI_DEBUG is not set
 # CONFIG_PCI_STUB is not set
 # CONFIG_PCI_IOV is not set
@@ -290,7 +285,6 @@ CONFIG_NET=y
 # Networking options
 #
 CONFIG_PACKET=y
-CONFIG_PACKET_MMAP=y
 CONFIG_UNIX=y
 CONFIG_XFRM=y
 CONFIG_XFRM_USER=m
@@ -425,10 +419,6 @@ CONFIG_BLK_DEV=y
 # CONFIG_BLK_DEV_COW_COMMON is not set
 CONFIG_BLK_DEV_LOOP=m
 CONFIG_BLK_DEV_CRYPTOLOOP=m
-
-#
-# DRBD disabled because PROC_FS, INET or CONNECTOR not selected
-#
 # CONFIG_BLK_DEV_DRBD is not set
 CONFIG_BLK_DEV_NBD=m
 # CONFIG_BLK_DEV_SX8 is not set
@@ -677,6 +667,7 @@ CONFIG_SUNGEM=m
 CONFIG_SUNVNET=m
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_ETHOC is not set
+# CONFIG_GRETH is not set
 # CONFIG_DNET is not set
 # CONFIG_NET_TULIP is not set
 # CONFIG_HP100 is not set
@@ -691,6 +682,7 @@ CONFIG_NET_PCI=y
 # CONFIG_PCNET32 is not set
 # CONFIG_AMD8111_ETH is not set
 # CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_KSZ884X_PCI is not set
 # CONFIG_B44 is not set
 # CONFIG_FORCEDETH is not set
 # CONFIG_E100 is not set
@@ -741,6 +733,7 @@ CONFIG_CHELSIO_T3_DEPENDS=y
 # CONFIG_CHELSIO_T3 is not set
 # CONFIG_ENIC is not set
 # CONFIG_IXGBE is not set
+# CONFIG_IXGBEVF is not set
 # CONFIG_IXGB is not set
 # CONFIG_S2IO is not set
 # CONFIG_VXGE is not set
@@ -751,6 +744,7 @@ CONFIG_NIU=m
 # CONFIG_MLX4_CORE is not set
 # CONFIG_TEHUTI is not set
 # CONFIG_BNX2X is not set
+# CONFIG_QLCNIC is not set
 # CONFIG_QLGE is not set
 # CONFIG_SFC is not set
 # CONFIG_BE2NET is not set
@@ -1028,6 +1022,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_SMSC47M192 is not set
 # CONFIG_SENSORS_SMSC47B397 is not set
 # CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_AMC6821 is not set
 # CONFIG_SENSORS_THMC50 is not set
 # CONFIG_SENSORS_TMP401 is not set
 # CONFIG_SENSORS_TMP421 is not set
@@ -1076,6 +1071,7 @@ CONFIG_SSB_POSSIBLE=y
 # Graphics support
 #
 CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
 # CONFIG_DRM is not set
 # CONFIG_VGASTATE is not set
 # CONFIG_VIDEO_OUTPUT_CONTROL is not set
@@ -1279,6 +1275,7 @@ CONFIG_SND_ALI5451=m
 # CONFIG_SND_YMFPCI is not set
 CONFIG_SND_USB=y
 # CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_UA101 is not set
 # CONFIG_SND_USB_CAIAQ is not set
 CONFIG_SND_SPARC=y
 # CONFIG_SND_SUN_AMD7930 is not set
@@ -1301,6 +1298,7 @@ CONFIG_USB_HIDDEV=y
 #
 # Special HID drivers
 #
+# CONFIG_HID_3M_PCT is not set
 CONFIG_HID_A4TECH=y
 CONFIG_HID_APPLE=y
 CONFIG_HID_BELKIN=y
@@ -1317,14 +1315,19 @@ CONFIG_HID_KENSINGTON=y
 CONFIG_HID_LOGITECH=y
 # CONFIG_LOGITECH_FF is not set
 # CONFIG_LOGIRUMBLEPAD2_FF is not set
+# CONFIG_LOGIG940_FF is not set
 CONFIG_HID_MICROSOFT=y
+# CONFIG_HID_MOSART is not set
 CONFIG_HID_MONTEREY=y
 CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
 CONFIG_HID_PANTHERLORD=y
 # CONFIG_PANTHERLORD_FF is not set
 CONFIG_HID_PETALYNX=y
+# CONFIG_HID_QUANTA is not set
 CONFIG_HID_SAMSUNG=y
 CONFIG_HID_SONY=y
+# CONFIG_HID_STANTUM is not set
 CONFIG_HID_SUNPLUS=y
 CONFIG_HID_GREENASIA=y
 # CONFIG_GREENASIA_FF is not set
@@ -1807,6 +1810,7 @@ CONFIG_CRYPTO_MANAGER=y
 CONFIG_CRYPTO_MANAGER2=y
 CONFIG_CRYPTO_GF128MUL=m
 CONFIG_CRYPTO_NULL=m
+# CONFIG_CRYPTO_PCRYPT is not set
 CONFIG_CRYPTO_WORKQUEUE=y
 # CONFIG_CRYPTO_CRYPTD is not set
 CONFIG_CRYPTO_AUTHENC=y
@@ -1904,9 +1908,11 @@ CONFIG_CRC32=y
 CONFIG_LIBCRC32C=m
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_DECOMPRESS=y
 CONFIG_DECOMPRESS_GZIP=y
 CONFIG_DECOMPRESS_BZIP2=y
 CONFIG_DECOMPRESS_LZMA=y
+CONFIG_DECOMPRESS_LZO=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT=y
 CONFIG_HAS_DMA=y
index 679c7504625acc86deab57d1e1feada4ef408317..2889574608db72f36b3ae1ea61309f77e22e282f 100644 (file)
@@ -249,10 +249,14 @@ extern void iounmap(volatile void __iomem *addr);
 
 #define ioread8(X)                     readb(X)
 #define ioread16(X)                    readw(X)
+#define ioread16be(X)                  __raw_readw(X)
 #define ioread32(X)                    readl(X)
+#define ioread32be(X)                  __raw_readl(X)
 #define iowrite8(val,X)                        writeb(val,X)
 #define iowrite16(val,X)               writew(val,X)
+#define iowrite16be(val,X)             __raw_writew(val,X)
 #define iowrite32(val,X)               writel(val,X)
+#define iowrite32be(val,X)             __raw_writel(val,X)
 
 static inline void ioread8_rep(void __iomem *port, void *buf, unsigned long count)
 {
index 4aee21dc9c6f10d8f6d0f908177f7c56f5095c72..9517d063c79c604d99d21967981f07b92cb1c70c 100644 (file)
@@ -468,10 +468,14 @@ static inline void iounmap(volatile void __iomem *addr)
 
 #define ioread8(X)                     readb(X)
 #define ioread16(X)                    readw(X)
+#define ioread16be(X)                  __raw_readw(X)
 #define ioread32(X)                    readl(X)
+#define ioread32be(X)                  __raw_readl(X)
 #define iowrite8(val,X)                        writeb(val,X)
 #define iowrite16(val,X)               writew(val,X)
+#define iowrite16be(val,X)             __raw_writew(val,X)
 #define iowrite32(val,X)               writel(val,X)
+#define iowrite32be(val,X)             __raw_writel(val,X)
 
 /* Create a virtual mapping cookie for an IO port range */
 extern void __iomem *ioport_map(unsigned long port, unsigned int nr);
index 836873002b7515cc92a727447d4b13775ee5202d..8d8720a8770de451e3e1911e189327caf03e4a2d 100644 (file)
@@ -10,8 +10,8 @@
  * from enumeration below.  The meaning of further arguments
  * are determined by the operation code.
  *
- * int sys_perfctr(int opcode, unsigned long arg0,
- *                 unsigned long arg1, unsigned long arg2)
+ * NOTE: This system call is no longer provided, use the perf_events
+ *       infrastructure.
  *
  * Pointers which are passed by the user are pointers to 64-bit
  * integers.
index d47a98e66972021afff27a0c4aeb0679855eaf81..d24cfe16afc1babc4feb78f0c697555754304346 100644 (file)
@@ -143,15 +143,7 @@ do {                                               \
         * and 2 stores in this critical code path.  -DaveM
         */
 #define switch_to(prev, next, last)                                    \
-do {   if (test_thread_flag(TIF_PERFCTR)) {                            \
-               unsigned long __tmp;                                    \
-               read_pcr(__tmp);                                        \
-               current_thread_info()->pcr_reg = __tmp;                 \
-               read_pic(__tmp);                                        \
-               current_thread_info()->kernel_cntd0 += (unsigned int)(__tmp);\
-               current_thread_info()->kernel_cntd1 += ((__tmp) >> 32); \
-       }                                                               \
-       flush_tlb_pending();                                            \
+do {   flush_tlb_pending();                                            \
        save_and_clear_fpu();                                           \
        /* If you are tempted to conditionalize the following */        \
        /* so that ASI is only written if it changes, think again. */   \
@@ -197,11 +189,6 @@ do {       if (test_thread_flag(TIF_PERFCTR)) {                            \
                "l1", "l2", "l3", "l4", "l5", "l6", "l7",               \
          "i0", "i1", "i2", "i3", "i4", "i5",                           \
          "o0", "o1", "o2", "o3", "o4", "o5",       "o7");              \
-       /* If you fuck with this, update ret_from_syscall code too. */  \
-       if (test_thread_flag(TIF_PERFCTR)) {                            \
-               write_pcr(current_thread_info()->pcr_reg);              \
-               reset_pic();                                            \
-       }                                                               \
 } while(0)
 
 static inline unsigned long xchg32(__volatile__ unsigned int *m, unsigned int val)
index 39be9f256e5a5b6f24d1f1604759b6cbad2b5f4b..9e2d9447f2ad094e7e1ef963675ee36fa51ba06f 100644 (file)
@@ -58,11 +58,6 @@ struct thread_info {
        unsigned long           gsr[7];
        unsigned long           xfsr[7];
 
-       __u64                   __user *user_cntd0;
-       __u64                   __user *user_cntd1;
-       __u64                   kernel_cntd0, kernel_cntd1;
-       __u64                   pcr_reg;
-
        struct restart_block    restart_block;
 
        struct pt_regs          *kern_una_regs;
@@ -96,15 +91,10 @@ struct thread_info {
 #define TI_RWIN_SPTRS  0x000003c8
 #define TI_GSR         0x00000400
 #define TI_XFSR                0x00000438
-#define TI_USER_CNTD0  0x00000470
-#define TI_USER_CNTD1  0x00000478
-#define TI_KERN_CNTD0  0x00000480
-#define TI_KERN_CNTD1  0x00000488
-#define TI_PCR         0x00000490
-#define TI_RESTART_BLOCK 0x00000498
-#define TI_KUNA_REGS   0x000004c8
-#define TI_KUNA_INSN   0x000004d0
-#define TI_FPREGS      0x00000500
+#define TI_RESTART_BLOCK 0x00000470
+#define TI_KUNA_REGS   0x000004a0
+#define TI_KUNA_INSN   0x000004a8
+#define TI_FPREGS      0x000004c0
 
 /* We embed this in the uppermost byte of thread_info->flags */
 #define FAULT_CODE_WRITE       0x01    /* Write access, implies D-TLB     */
@@ -199,7 +189,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
  *
  * On trap return we need to test several values:
  *
- * user:       need_resched, notify_resume, sigpending, wsaved, perfctr
+ * user:       need_resched, notify_resume, sigpending, wsaved
  * kernel:     fpdepth
  *
  * So to check for work in the kernel case we simply load the fpdepth
@@ -220,7 +210,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define TIF_NOTIFY_RESUME      1       /* callback before returning to user */
 #define TIF_SIGPENDING         2       /* signal pending */
 #define TIF_NEED_RESCHED       3       /* rescheduling necessary */
-#define TIF_PERFCTR            4       /* performance counters active */
+/* flag bit 4 is available */
 #define TIF_UNALIGNED          5       /* allowed to do unaligned accesses */
 /* flag bit 6 is available */
 #define TIF_32BIT              7       /* 32-bit binary */
@@ -241,7 +231,6 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define _TIF_NOTIFY_RESUME     (1<<TIF_NOTIFY_RESUME)
 #define _TIF_SIGPENDING                (1<<TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED      (1<<TIF_NEED_RESCHED)
-#define _TIF_PERFCTR           (1<<TIF_PERFCTR)
 #define _TIF_UNALIGNED         (1<<TIF_UNALIGNED)
 #define _TIF_32BIT             (1<<TIF_32BIT)
 #define _TIF_SECCOMP           (1<<TIF_SECCOMP)
@@ -252,7 +241,7 @@ register struct thread_info *current_thread_info_reg asm("g6");
 
 #define _TIF_USER_WORK_MASK    ((0xff << TI_FLAG_WSAVED_SHIFT) | \
                                 _TIF_DO_NOTIFY_RESUME_MASK | \
-                                _TIF_NEED_RESCHED | _TIF_PERFCTR)
+                                _TIF_NEED_RESCHED)
 #define _TIF_DO_NOTIFY_RESUME_MASK     (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING)
 
 /*
index 4f53a2395ac6364ed3f52f8226dc94ecb7e5fc4e..c011b932bb17dd8450ac94527ab73c63c5e51b38 100644 (file)
@@ -48,7 +48,6 @@ extern void __init boot_cpu_id_too_large(int cpu);
 extern unsigned int dcache_parity_tl1_occurred;
 extern unsigned int icache_parity_tl1_occurred;
 
-extern asmlinkage void update_perfctrs(void);
 extern asmlinkage void sparc_breakpoint(struct pt_regs *regs);
 extern void timer_interrupt(int irq, struct pt_regs *regs);
 
index cb70476bd8f5ccd72a6f72900195f8a74f6abfcb..a5cf3864b31f87dcabf7abe4ea1e96be693b54b4 100644 (file)
@@ -352,12 +352,6 @@ void exit_thread(void)
                else
                        t->utraps[0]--;
        }
-
-       if (test_and_clear_thread_flag(TIF_PERFCTR)) {
-               t->user_cntd0 = t->user_cntd1 = NULL;
-               t->pcr_reg = 0;
-               write_pcr(0);
-       }
 }
 
 void flush_thread(void)
@@ -371,13 +365,6 @@ void flush_thread(void)
 
        set_thread_wsaved(0);
 
-       /* Turn off performance counters if on. */
-       if (test_and_clear_thread_flag(TIF_PERFCTR)) {
-               t->user_cntd0 = t->user_cntd1 = NULL;
-               t->pcr_reg = 0;
-               write_pcr(0);
-       }
-
        /* Clear FPU register state. */
        t->fpsaved[0] = 0;
        
@@ -591,16 +578,6 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
                t->kregs->u_regs[UREG_FP] =
                  ((unsigned long) child_sf) - STACK_BIAS;
 
-               /* Special case, if we are spawning a kernel thread from
-                * a userspace task (usermode helper, NFS or similar), we
-                * must disable performance counters in the child because
-                * the address space and protection realm are changing.
-                */
-               if (t->flags & _TIF_PERFCTR) {
-                       t->user_cntd0 = t->user_cntd1 = NULL;
-                       t->pcr_reg = 0;
-                       t->flags &= ~_TIF_PERFCTR;
-               }
                t->flags |= ((long)ASI_P << TI_FLAG_CURRENT_DS_SHIFT);
                t->kregs->u_regs[UREG_G6] = (unsigned long) t;
                t->kregs->u_regs[UREG_G4] = (unsigned long) t->task;
index 1ddec403f512b9650620a8827da688f71771b8e9..83f1873c6c131bfc68b21290fbe0bb3bfb9ffefb 100644 (file)
@@ -65,48 +65,6 @@ __handle_user_windows:
                ba,pt                   %xcc, __handle_user_windows_continue
 
                 andn                   %l1, %l4, %l1
-__handle_perfctrs:
-               call                    update_perfctrs
-                wrpr                   %g0, RTRAP_PSTATE, %pstate
-               wrpr                    %g0, RTRAP_PSTATE_IRQOFF, %pstate
-               ldub                    [%g6 + TI_WSAVED], %o2
-               brz,pt                  %o2, 1f
-                nop
-               /* Redo userwin+sched+sig checks */
-               call                    fault_in_user_windows
-
-                wrpr                   %g0, RTRAP_PSTATE, %pstate
-               wrpr                    %g0, RTRAP_PSTATE_IRQOFF, %pstate
-               ldx                     [%g6 + TI_FLAGS], %l0
-               andcc                   %l0, _TIF_NEED_RESCHED, %g0
-               be,pt                   %xcc, 1f
-
-                nop
-               call                    schedule
-                wrpr                   %g0, RTRAP_PSTATE, %pstate
-               wrpr                    %g0, RTRAP_PSTATE_IRQOFF, %pstate
-               ldx                     [%g6 + TI_FLAGS], %l0
-1:             andcc                   %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
-
-               be,pt                   %xcc, __handle_perfctrs_continue
-                sethi                  %hi(TSTATE_PEF), %o0
-               mov                     %l5, %o1
-               add                     %sp, PTREGS_OFF, %o0
-               mov                     %l0, %o2
-               call                    do_notify_resume
-
-                wrpr                   %g0, RTRAP_PSTATE, %pstate
-               wrpr                    %g0, RTRAP_PSTATE_IRQOFF, %pstate
-               /* Signal delivery can modify pt_regs tstate, so we must
-                * reload it.
-                */
-               ldx                     [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
-               sethi                   %hi(0xf << 20), %l4
-               and                     %l1, %l4, %l4
-               andn                    %l1, %l4, %l1
-               ba,pt                   %xcc, __handle_perfctrs_continue
-
-                sethi                  %hi(TSTATE_PEF), %o0
 __handle_userfpu:
                rd                      %fprs, %l5
                andcc                   %l5, FPRS_FEF, %g0
@@ -191,9 +149,9 @@ rtrap_no_irq_enable:
                 * take until the next local IRQ before the signal/resched
                 * event would be handled.
                 *
-                * This also means that if we have to deal with performance
-                * counters or user windows, we have to redo all of these
-                * sched+signal checks with IRQs disabled.
+                * This also means that if we have to deal with user
+                * windows, we have to redo all of these sched+signal checks
+                * with IRQs disabled.
                 */
 to_user:       wrpr                    %g0, RTRAP_PSTATE_IRQOFF, %pstate
                wrpr                    0, %pil
@@ -214,12 +172,8 @@ __handle_signal_continue:
                brnz,pn                 %o2, __handle_user_windows
                 nop
 __handle_user_windows_continue:
-               ldx                     [%g6 + TI_FLAGS], %l5
-               andcc                   %l5, _TIF_PERFCTR, %g0
                sethi                   %hi(TSTATE_PEF), %o0
-               bne,pn                  %xcc, __handle_perfctrs
-__handle_perfctrs_continue:
-                andcc                  %l1, %o0, %g0
+               andcc                   %l1, %o0, %g0
 
                /* This fpdepth clear is necessary for non-syscall rtraps only */
 user_nowork:
index e7061138c98a03ec15b51eca77877144a9ddd9b9..46a76ba3fb4bd21d2b911b9ce99cf5f235264b68 100644 (file)
@@ -51,7 +51,6 @@ SIGN1(sys32_exit_group, sys_exit_group, %o0)
 SIGN1(sys32_wait4, compat_sys_wait4, %o2)
 SIGN1(sys32_creat, sys_creat, %o1)
 SIGN1(sys32_mknod, sys_mknod, %o1)
-SIGN1(sys32_perfctr, sys_perfctr, %o0)
 SIGN1(sys32_umount, sys_umount, %o1)
 SIGN1(sys32_signal, sys_signal, %o0)
 SIGN1(sys32_access, sys_access, %o1)
index d77f54316948e83cdc035c9e04d76cbd8d4702a1..cb1bef6f14b7240e3f87bb693b060fcbcb90a081 100644 (file)
@@ -27,7 +27,6 @@
 
 #include <asm/uaccess.h>
 #include <asm/utrap.h>
-#include <asm/perfctr.h>
 #include <asm/unistd.h>
 
 #include "entry.h"
@@ -766,109 +765,6 @@ SYSCALL_DEFINE5(rt_sigaction, int, sig, const struct sigaction __user *, act,
        return ret;
 }
 
-/* Invoked by rtrap code to update performance counters in
- * user space.
- */
-asmlinkage void update_perfctrs(void)
-{
-       unsigned long pic, tmp;
-
-       read_pic(pic);
-       tmp = (current_thread_info()->kernel_cntd0 += (unsigned int)pic);
-       __put_user(tmp, current_thread_info()->user_cntd0);
-       tmp = (current_thread_info()->kernel_cntd1 += (pic >> 32));
-       __put_user(tmp, current_thread_info()->user_cntd1);
-       reset_pic();
-}
-
-SYSCALL_DEFINE4(perfctr, int, opcode, unsigned long, arg0,
-               unsigned long, arg1, unsigned long, arg2)
-{
-       int err = 0;
-
-       switch(opcode) {
-       case PERFCTR_ON:
-               current_thread_info()->pcr_reg = arg2;
-               current_thread_info()->user_cntd0 = (u64 __user *) arg0;
-               current_thread_info()->user_cntd1 = (u64 __user *) arg1;
-               current_thread_info()->kernel_cntd0 =
-                       current_thread_info()->kernel_cntd1 = 0;
-               write_pcr(arg2);
-               reset_pic();
-               set_thread_flag(TIF_PERFCTR);
-               break;
-
-       case PERFCTR_OFF:
-               err = -EINVAL;
-               if (test_thread_flag(TIF_PERFCTR)) {
-                       current_thread_info()->user_cntd0 =
-                               current_thread_info()->user_cntd1 = NULL;
-                       current_thread_info()->pcr_reg = 0;
-                       write_pcr(0);
-                       clear_thread_flag(TIF_PERFCTR);
-                       err = 0;
-               }
-               break;
-
-       case PERFCTR_READ: {
-               unsigned long pic, tmp;
-
-               if (!test_thread_flag(TIF_PERFCTR)) {
-                       err = -EINVAL;
-                       break;
-               }
-               read_pic(pic);
-               tmp = (current_thread_info()->kernel_cntd0 += (unsigned int)pic);
-               err |= __put_user(tmp, current_thread_info()->user_cntd0);
-               tmp = (current_thread_info()->kernel_cntd1 += (pic >> 32));
-               err |= __put_user(tmp, current_thread_info()->user_cntd1);
-               reset_pic();
-               break;
-       }
-
-       case PERFCTR_CLRPIC:
-               if (!test_thread_flag(TIF_PERFCTR)) {
-                       err = -EINVAL;
-                       break;
-               }
-               current_thread_info()->kernel_cntd0 =
-                       current_thread_info()->kernel_cntd1 = 0;
-               reset_pic();
-               break;
-
-       case PERFCTR_SETPCR: {
-               u64 __user *user_pcr = (u64 __user *)arg0;
-
-               if (!test_thread_flag(TIF_PERFCTR)) {
-                       err = -EINVAL;
-                       break;
-               }
-               err |= __get_user(current_thread_info()->pcr_reg, user_pcr);
-               write_pcr(current_thread_info()->pcr_reg);
-               current_thread_info()->kernel_cntd0 =
-                       current_thread_info()->kernel_cntd1 = 0;
-               reset_pic();
-               break;
-       }
-
-       case PERFCTR_GETPCR: {
-               u64 __user *user_pcr = (u64 __user *)arg0;
-
-               if (!test_thread_flag(TIF_PERFCTR)) {
-                       err = -EINVAL;
-                       break;
-               }
-               err |= __put_user(current_thread_info()->pcr_reg, user_pcr);
-               break;
-       }
-
-       default:
-               err = -EINVAL;
-               break;
-       };
-       return err;
-}
-
 /*
  * Do a system call from kernel instead of calling sys_execve so we
  * end up with proper pt_regs.
index dc4a458f74dcc590a9cbe391dbc14a7b8302353c..1d7e274f3f2b42bfac80354f1745c7ad48cd9e27 100644 (file)
@@ -110,31 +110,12 @@ sys_clone:
 
        .globl  ret_from_syscall
 ret_from_syscall:
-       /* Clear current_thread_info()->new_child, and
-        * check performance counter stuff too.
-        */
+       /* Clear current_thread_info()->new_child. */
        stb     %g0, [%g6 + TI_NEW_CHILD]
        ldx     [%g6 + TI_FLAGS], %l0
        call    schedule_tail
         mov    %g7, %o0
-       andcc   %l0, _TIF_PERFCTR, %g0
-       be,pt   %icc, 1f
-        nop
-       ldx     [%g6 + TI_PCR], %o7
-       wr      %g0, %o7, %pcr
-
-       /* Blackbird errata workaround.  See commentary in
-        * smp.c:smp_percpu_timer_interrupt() for more
-        * information.
-        */
-       ba,pt   %xcc, 99f
-        nop
-
-       .align  64
-99:    wr      %g0, %g0, %pic
-       rd      %pic, %g0
-
-1:     ba,pt   %xcc, ret_sys_call
+       ba,pt   %xcc, ret_sys_call
         ldx    [%sp + PTREGS_OFF + PT_V9_I0], %o0
 
        .globl  sparc_exit
index d2f999ae2b85a04495d31ab2c77ad91924342f5a..68312fe8da740fa967ea3f90bd1c6cd15a7cf8a4 100644 (file)
@@ -36,8 +36,6 @@ extern asmlinkage long sys_rt_sigaction(int sig,
                                        struct sigaction __user *oact,
                                        void __user *restorer,
                                        size_t sigsetsize);
-extern asmlinkage long sys_perfctr(int opcode, unsigned long arg0,
-                                  unsigned long arg1, unsigned long arg2);
 
 extern asmlinkage void sparc64_set_context(struct pt_regs *regs);
 extern asmlinkage void sparc64_get_context(struct pt_regs *regs);
index e575b46bd7a9ebd84eb29578ef0e04952d07fff2..17614251fb6d8cb34cdc79722f1e3137b006c7a2 100644 (file)
@@ -21,7 +21,7 @@ sys_call_table32:
 /*0*/  .word sys_restart_syscall, sys32_exit, sys_fork, sys_read, sys_write
 /*5*/  .word sys32_open, sys_close, sys32_wait4, sys32_creat, sys_link
 /*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod
-/*15*/ .word sys_chmod, sys_lchown16, sys_brk, sys32_perfctr, sys32_lseek
+/*15*/ .word sys_chmod, sys_lchown16, sys_brk, sys_nis_syscall, sys32_lseek
 /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16
 /*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys_pause
 /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice
@@ -96,7 +96,7 @@ sys_call_table:
 /*0*/  .word sys_restart_syscall, sparc_exit, sys_fork, sys_read, sys_write
 /*5*/  .word sys_open, sys_close, sys_wait4, sys_creat, sys_link
 /*10*/  .word sys_unlink, sys_nis_syscall, sys_chdir, sys_chown, sys_mknod
-/*15*/ .word sys_chmod, sys_lchown, sys_brk, sys_perfctr, sys_lseek
+/*15*/ .word sys_chmod, sys_lchown, sys_brk, sys_nis_syscall, sys_lseek
 /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid, sys_getuid
 /*25*/ .word sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall
 /*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
index 10f7bb9fc1404e77a6eb542dec98285e98762162..bdc05a21908b98afab4189b7307a72a6b81a985f 100644 (file)
@@ -2548,15 +2548,6 @@ void __init trap_init(void)
                                               rwbuf_stkptrs) ||
                     TI_GSR != offsetof(struct thread_info, gsr) ||
                     TI_XFSR != offsetof(struct thread_info, xfsr) ||
-                    TI_USER_CNTD0 != offsetof(struct thread_info,
-                                              user_cntd0) ||
-                    TI_USER_CNTD1 != offsetof(struct thread_info,
-                                              user_cntd1) ||
-                    TI_KERN_CNTD0 != offsetof(struct thread_info,
-                                              kernel_cntd0) ||
-                    TI_KERN_CNTD1 != offsetof(struct thread_info,
-                                              kernel_cntd1) ||
-                    TI_PCR != offsetof(struct thread_info, pcr_reg) ||
                     TI_PRE_COUNT != offsetof(struct thread_info,
                                              preempt_count) ||
                     TI_NEW_CHILD != offsetof(struct thread_info, new_child) ||
index 4b7c937bba61704cf386e76c7907bbdcf1bcb759..2d8b70d397f154318612858c1bf7811160ee7f49 100644 (file)
@@ -32,10 +32,9 @@ extern void prom_cif_interface(void);
 extern void prom_cif_callback(void);
 
 /*
- * This provides SMP safety on the p1275buf. prom_callback() drops this lock
- * to allow recursuve acquisition.
+ * This provides SMP safety on the p1275buf.
  */
-DEFINE_SPINLOCK(prom_entry_lock);
+DEFINE_RAW_SPINLOCK(prom_entry_lock);
 
 long p1275_cmd(const char *service, long fmt, ...)
 {
@@ -47,7 +46,9 @@ long p1275_cmd(const char *service, long fmt, ...)
        
        p = p1275buf.prom_buffer;
 
-       spin_lock_irqsave(&prom_entry_lock, flags);
+       raw_local_save_flags(flags);
+       raw_local_irq_restore(PIL_NMI);
+       raw_spin_lock(&prom_entry_lock);
 
        p1275buf.prom_args[0] = (unsigned long)p;               /* service */
        strcpy (p, service);
@@ -139,7 +140,8 @@ long p1275_cmd(const char *service, long fmt, ...)
        va_end(list);
        x = p1275buf.prom_args [nargs + 3];
 
-       spin_unlock_irqrestore(&prom_entry_lock, flags);
+       raw_spin_unlock(&prom_entry_lock);
+       raw_local_irq_restore(flags);
 
        return x;
 }
diff --git a/arch/um/.gitignore b/arch/um/.gitignore
new file mode 100644 (file)
index 0000000..a73d3a1
--- /dev/null
@@ -0,0 +1,3 @@
+kernel/config.c
+kernel/config.tmp
+kernel/vmlinux.lds
index cf8a97f3451856665b11fdb6172b67953d6ee513..64cda95f59ca35db49675b90c8b8f1d6447f5c16 100644 (file)
@@ -18,10 +18,10 @@ static irqreturn_t line_interrupt(int irq, void *data)
 {
        struct chan *chan = data;
        struct line *line = chan->line;
-       struct tty_struct *tty = line->tty;
+       struct tty_struct *tty;
 
        if (line)
-               chan_interrupt(&line->chan_list, &line->task, tty, irq);
+               chan_interrupt(&line->chan_list, &line->task, line->tty, irq);
        return IRQ_HANDLED;
 }
 
index 3b3c36601a7b39e3634458eb11c5b0245261cb3e..de317d0c329486b57efb3568f7c4514393ac5677 100644 (file)
@@ -140,7 +140,7 @@ void mconsole_proc(struct mc_request *req)
                goto out;
        }
 
-       err = may_open(&nd.path, MAY_READ, FMODE_READ);
+       err = may_open(&nd.path, MAY_READ, O_RDONLY);
        if (result) {
                mconsole_reply(req, "Failed to open file", 1, 0);
                path_put(&nd.path);
index 1b549bca46454c2098974396af7db246dc27a677..804b28dd0328a30088e122a1dcd984bf1464ba75 100644 (file)
@@ -6,6 +6,8 @@ obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
        ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \
        sys_call_table.o tls.o
 
+obj-$(CONFIG_BINFMT_ELF) += elfcore.o
+
 subarch-obj-y = lib/semaphore_32.o lib/string_32.o
 subarch-obj-$(CONFIG_HIGHMEM) += mm/highmem_32.o
 subarch-obj-$(CONFIG_MODULES) += kernel/module.o
index 770885472ed4c850b59d5fea34ad1c0c5db1439f..e64cd41d7babad9d9eaee2afb010e590d242aaf6 100644 (file)
@@ -116,47 +116,4 @@ do {                                                               \
        }                                                       \
 } while (0)
 
-/*
- * These macros parameterize elf_core_dump in fs/binfmt_elf.c to write out
- * extra segments containing the vsyscall DSO contents.  Dumping its
- * contents makes post-mortem fully interpretable later without matching up
- * the same kernel and hardware config to see what PC values meant.
- * Dumping its extra ELF program headers includes all the other information
- * a debugger needs to easily find how the vsyscall DSO was being used.
- */
-#define ELF_CORE_EXTRA_PHDRS                                                 \
-       (vsyscall_ehdr ? (((struct elfhdr *)vsyscall_ehdr)->e_phnum) : 0 )
-
-#define ELF_CORE_WRITE_EXTRA_PHDRS                                           \
-if ( vsyscall_ehdr ) {                                                       \
-       const struct elfhdr *const ehdrp = (struct elfhdr *)vsyscall_ehdr;    \
-       const struct elf_phdr *const phdrp =                                  \
-               (const struct elf_phdr *) (vsyscall_ehdr + ehdrp->e_phoff);   \
-       int i;                                                                \
-       Elf32_Off ofs = 0;                                                    \
-       for (i = 0; i < ehdrp->e_phnum; ++i) {                                \
-               struct elf_phdr phdr = phdrp[i];                              \
-               if (phdr.p_type == PT_LOAD) {                                 \
-                       ofs = phdr.p_offset = offset;                         \
-                       offset += phdr.p_filesz;                              \
-               }                                                             \
-               else                                                          \
-                       phdr.p_offset += ofs;                                 \
-               phdr.p_paddr = 0; /* match other core phdrs */                \
-               DUMP_WRITE(&phdr, sizeof(phdr));                              \
-       }                                                                     \
-}
-#define ELF_CORE_WRITE_EXTRA_DATA                                            \
-if ( vsyscall_ehdr ) {                                                       \
-       const struct elfhdr *const ehdrp = (struct elfhdr *)vsyscall_ehdr;    \
-       const struct elf_phdr *const phdrp =                                  \
-               (const struct elf_phdr *) (vsyscall_ehdr + ehdrp->e_phoff);   \
-       int i;                                                                \
-       for (i = 0; i < ehdrp->e_phnum; ++i) {                                \
-               if (phdrp[i].p_type == PT_LOAD)                               \
-                       DUMP_WRITE((void *) phdrp[i].p_vaddr,                 \
-                                  phdrp[i].p_filesz);                        \
-       }                                                                     \
-}
-
 #endif
diff --git a/arch/um/sys-i386/elfcore.c b/arch/um/sys-i386/elfcore.c
new file mode 100644 (file)
index 0000000..6bb49b6
--- /dev/null
@@ -0,0 +1,83 @@
+#include <linux/elf.h>
+#include <linux/coredump.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+#include <asm/elf.h>
+
+
+Elf32_Half elf_core_extra_phdrs(void)
+{
+       return vsyscall_ehdr ? (((struct elfhdr *)vsyscall_ehdr)->e_phnum) : 0;
+}
+
+int elf_core_write_extra_phdrs(struct file *file, loff_t offset, size_t *size,
+                              unsigned long limit)
+{
+       if ( vsyscall_ehdr ) {
+               const struct elfhdr *const ehdrp =
+                       (struct elfhdr *) vsyscall_ehdr;
+               const struct elf_phdr *const phdrp =
+                       (const struct elf_phdr *) (vsyscall_ehdr + ehdrp->e_phoff);
+               int i;
+               Elf32_Off ofs = 0;
+
+               for (i = 0; i < ehdrp->e_phnum; ++i) {
+                       struct elf_phdr phdr = phdrp[i];
+
+                       if (phdr.p_type == PT_LOAD) {
+                               ofs = phdr.p_offset = offset;
+                               offset += phdr.p_filesz;
+                       } else {
+                               phdr.p_offset += ofs;
+                       }
+                       phdr.p_paddr = 0; /* match other core phdrs */
+                       *size += sizeof(phdr);
+                       if (*size > limit
+                           || !dump_write(file, &phdr, sizeof(phdr)))
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+int elf_core_write_extra_data(struct file *file, size_t *size,
+                             unsigned long limit)
+{
+       if ( vsyscall_ehdr ) {
+               const struct elfhdr *const ehdrp =
+                       (struct elfhdr *) vsyscall_ehdr;
+               const struct elf_phdr *const phdrp =
+                       (const struct elf_phdr *) (vsyscall_ehdr + ehdrp->e_phoff);
+               int i;
+
+               for (i = 0; i < ehdrp->e_phnum; ++i) {
+                       if (phdrp[i].p_type == PT_LOAD) {
+                               void *addr = (void *) phdrp[i].p_vaddr;
+                               size_t filesz = phdrp[i].p_filesz;
+
+                               *size += filesz;
+                               if (*size > limit
+                                   || !dump_write(file, addr, filesz))
+                                       return 0;
+                       }
+               }
+       }
+       return 1;
+}
+
+size_t elf_core_extra_data_size(void)
+{
+       if ( vsyscall_ehdr ) {
+               const struct elfhdr *const ehdrp =
+                       (struct elfhdr *)vsyscall_ehdr;
+               const struct elf_phdr *const phdrp =
+                       (const struct elf_phdr *) (vsyscall_ehdr + ehdrp->e_phoff);
+               int i;
+
+               for (i = 0; i < ehdrp->e_phnum; ++i)
+                       if (phdrp[i].p_type == PT_LOAD)
+                               return (size_t) phdrp[i].p_filesz;
+       }
+       return 0;
+}
index 57ccdcec1469e675fe55c6f3a52d7df7b1367c6c..f15f37bfbd62b8f7a9ebc0ebb4e391cb1ea4df37 100644 (file)
@@ -31,6 +31,7 @@ config X86
        select ARCH_WANT_FRAME_POINTERS
        select HAVE_DMA_ATTRS
        select HAVE_KRETPROBES
+       select HAVE_OPTPROBES
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_DYNAMIC_FTRACE
        select HAVE_FUNCTION_TRACER
index 9f828f87ca35f418d24d4b7674477f643eea773d..493092efaa3bb67034a535634e15043660881be9 100644 (file)
@@ -11,6 +11,7 @@ header-y += sigcontext32.h
 header-y += ucontext.h
 header-y += processor-flags.h
 header-y += hw_breakpoint.h
+header-y += hyperv.h
 
 unifdef-y += e820.h
 unifdef-y += ist.h
index f1e253ceba4be4b4835bd78250bc6b19f3f8c23a..b09ec55650b3806f91f4909558a61688a39404ee 100644 (file)
@@ -165,10 +165,12 @@ static inline void apply_paravirt(struct paravirt_patch_site *start,
  * invalid instruction possible) or if the instructions are changed from a
  * consistent state to another consistent state atomically.
  * More care must be taken when modifying code in the SMP case because of
- * Intel's errata.
+ * Intel's errata. text_poke_smp() takes care that errata, but still
+ * doesn't support NMI/MCE handler code modifying.
  * On the local CPU you need to be protected again NMI or MCE handlers seeing an
  * inconsistent instruction while you patch.
  */
 extern void *text_poke(void *addr, const void *opcode, size_t len);
+extern void *text_poke_smp(void *addr, const void *opcode, size_t len);
 
 #endif /* _ASM_X86_ALTERNATIVE_H */
diff --git a/arch/x86/include/asm/hyperv.h b/arch/x86/include/asm/hyperv.h
new file mode 100644 (file)
index 0000000..e153a2b
--- /dev/null
@@ -0,0 +1,186 @@
+#ifndef _ASM_X86_KVM_HYPERV_H
+#define _ASM_X86_KVM_HYPERV_H
+
+#include <linux/types.h>
+
+/*
+ * The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
+ * is set by CPUID(HvCpuIdFunctionVersionAndFeatures).
+ */
+#define HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS  0x40000000
+#define HYPERV_CPUID_INTERFACE                 0x40000001
+#define HYPERV_CPUID_VERSION                   0x40000002
+#define HYPERV_CPUID_FEATURES                  0x40000003
+#define HYPERV_CPUID_ENLIGHTMENT_INFO          0x40000004
+#define HYPERV_CPUID_IMPLEMENT_LIMITS          0x40000005
+
+/*
+ * Feature identification. EAX indicates which features are available
+ * to the partition based upon the current partition privileges.
+ */
+
+/* VP Runtime (HV_X64_MSR_VP_RUNTIME) available */
+#define HV_X64_MSR_VP_RUNTIME_AVAILABLE                (1 << 0)
+/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/
+#define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE    (1 << 1)
+/*
+ * Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM
+ * and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available
+ */
+#define HV_X64_MSR_SYNIC_AVAILABLE             (1 << 2)
+/*
+ * Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through
+ * HV_X64_MSR_STIMER3_COUNT) available
+ */
+#define HV_X64_MSR_SYNTIMER_AVAILABLE          (1 << 3)
+/*
+ * APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR)
+ * are available
+ */
+#define HV_X64_MSR_APIC_ACCESS_AVAILABLE       (1 << 4)
+/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/
+#define HV_X64_MSR_HYPERCALL_AVAILABLE         (1 << 5)
+/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) available*/
+#define HV_X64_MSR_VP_INDEX_AVAILABLE          (1 << 6)
+/* Virtual system reset MSR (HV_X64_MSR_RESET) is available*/
+#define HV_X64_MSR_RESET_AVAILABLE             (1 << 7)
+ /*
+  * Access statistics pages MSRs (HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE,
+  * HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, HV_X64_MSR_STATS_VP_RETAIL_PAGE,
+  * HV_X64_MSR_STATS_VP_INTERNAL_PAGE) available
+  */
+#define HV_X64_MSR_STAT_PAGES_AVAILABLE                (1 << 8)
+
+/*
+ * Feature identification: EBX indicates which flags were specified at
+ * partition creation. The format is the same as the partition creation
+ * flag structure defined in section Partition Creation Flags.
+ */
+#define HV_X64_CREATE_PARTITIONS               (1 << 0)
+#define HV_X64_ACCESS_PARTITION_ID             (1 << 1)
+#define HV_X64_ACCESS_MEMORY_POOL              (1 << 2)
+#define HV_X64_ADJUST_MESSAGE_BUFFERS          (1 << 3)
+#define HV_X64_POST_MESSAGES                   (1 << 4)
+#define HV_X64_SIGNAL_EVENTS                   (1 << 5)
+#define HV_X64_CREATE_PORT                     (1 << 6)
+#define HV_X64_CONNECT_PORT                    (1 << 7)
+#define HV_X64_ACCESS_STATS                    (1 << 8)
+#define HV_X64_DEBUGGING                       (1 << 11)
+#define HV_X64_CPU_POWER_MANAGEMENT            (1 << 12)
+#define HV_X64_CONFIGURE_PROFILER              (1 << 13)
+
+/*
+ * Feature identification. EDX indicates which miscellaneous features
+ * are available to the partition.
+ */
+/* The MWAIT instruction is available (per section MONITOR / MWAIT) */
+#define HV_X64_MWAIT_AVAILABLE                         (1 << 0)
+/* Guest debugging support is available */
+#define HV_X64_GUEST_DEBUGGING_AVAILABLE               (1 << 1)
+/* Performance Monitor support is available*/
+#define HV_X64_PERF_MONITOR_AVAILABLE                  (1 << 2)
+/* Support for physical CPU dynamic partitioning events is available*/
+#define HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE      (1 << 3)
+/*
+ * Support for passing hypercall input parameter block via XMM
+ * registers is available
+ */
+#define HV_X64_HYPERCALL_PARAMS_XMM_AVAILABLE          (1 << 4)
+/* Support for a virtual guest idle state is available */
+#define HV_X64_GUEST_IDLE_STATE_AVAILABLE              (1 << 5)
+
+/*
+ * Implementation recommendations. Indicates which behaviors the hypervisor
+ * recommends the OS implement for optimal performance.
+ */
+ /*
+  * Recommend using hypercall for address space switches rather
+  * than MOV to CR3 instruction
+  */
+#define HV_X64_MWAIT_RECOMMENDED               (1 << 0)
+/* Recommend using hypercall for local TLB flushes rather
+ * than INVLPG or MOV to CR3 instructions */
+#define HV_X64_LOCAL_TLB_FLUSH_RECOMMENDED     (1 << 1)
+/*
+ * Recommend using hypercall for remote TLB flushes rather
+ * than inter-processor interrupts
+ */
+#define HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED    (1 << 2)
+/*
+ * Recommend using MSRs for accessing APIC registers
+ * EOI, ICR and TPR rather than their memory-mapped counterparts
+ */
+#define HV_X64_APIC_ACCESS_RECOMMENDED         (1 << 3)
+/* Recommend using the hypervisor-provided MSR to initiate a system RESET */
+#define HV_X64_SYSTEM_RESET_RECOMMENDED                (1 << 4)
+/*
+ * Recommend using relaxed timing for this partition. If used,
+ * the VM should disable any watchdog timeouts that rely on the
+ * timely delivery of external interrupts
+ */
+#define HV_X64_RELAXED_TIMING_RECOMMENDED      (1 << 5)
+
+/* MSR used to identify the guest OS. */
+#define HV_X64_MSR_GUEST_OS_ID                 0x40000000
+
+/* MSR used to setup pages used to communicate with the hypervisor. */
+#define HV_X64_MSR_HYPERCALL                   0x40000001
+
+/* MSR used to provide vcpu index */
+#define HV_X64_MSR_VP_INDEX                    0x40000002
+
+/* Define the virtual APIC registers */
+#define HV_X64_MSR_EOI                         0x40000070
+#define HV_X64_MSR_ICR                         0x40000071
+#define HV_X64_MSR_TPR                         0x40000072
+#define HV_X64_MSR_APIC_ASSIST_PAGE            0x40000073
+
+/* Define synthetic interrupt controller model specific registers. */
+#define HV_X64_MSR_SCONTROL                    0x40000080
+#define HV_X64_MSR_SVERSION                    0x40000081
+#define HV_X64_MSR_SIEFP                       0x40000082
+#define HV_X64_MSR_SIMP                                0x40000083
+#define HV_X64_MSR_EOM                         0x40000084
+#define HV_X64_MSR_SINT0                       0x40000090
+#define HV_X64_MSR_SINT1                       0x40000091
+#define HV_X64_MSR_SINT2                       0x40000092
+#define HV_X64_MSR_SINT3                       0x40000093
+#define HV_X64_MSR_SINT4                       0x40000094
+#define HV_X64_MSR_SINT5                       0x40000095
+#define HV_X64_MSR_SINT6                       0x40000096
+#define HV_X64_MSR_SINT7                       0x40000097
+#define HV_X64_MSR_SINT8                       0x40000098
+#define HV_X64_MSR_SINT9                       0x40000099
+#define HV_X64_MSR_SINT10                      0x4000009A
+#define HV_X64_MSR_SINT11                      0x4000009B
+#define HV_X64_MSR_SINT12                      0x4000009C
+#define HV_X64_MSR_SINT13                      0x4000009D
+#define HV_X64_MSR_SINT14                      0x4000009E
+#define HV_X64_MSR_SINT15                      0x4000009F
+
+
+#define HV_X64_MSR_HYPERCALL_ENABLE            0x00000001
+#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT        12
+#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \
+               (~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1))
+
+/* Declare the various hypercall operations. */
+#define HV_X64_HV_NOTIFY_LONG_SPIN_WAIT                0x0008
+
+#define HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE             0x00000001
+#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT      12
+#define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK       \
+               (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1))
+
+#define HV_PROCESSOR_POWER_STATE_C0            0
+#define HV_PROCESSOR_POWER_STATE_C1            1
+#define HV_PROCESSOR_POWER_STATE_C2            2
+#define HV_PROCESSOR_POWER_STATE_C3            3
+
+/* hypercall status code */
+#define HV_STATUS_SUCCESS                      0
+#define HV_STATUS_INVALID_HYPERCALL_CODE       2
+#define HV_STATUS_INVALID_HYPERCALL_INPUT      3
+#define HV_STATUS_INVALID_ALIGNMENT            4
+
+#endif
index 4fe681de1e76c164eedb543bad7333aa57de4b6d..4ffa345a8ccbd7d2b7db8debd7caa090aceaf8a5 100644 (file)
@@ -32,7 +32,10 @@ struct kprobe;
 
 typedef u8 kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION 0xcc
-#define RELATIVEJUMP_INSTRUCTION 0xe9
+#define RELATIVEJUMP_OPCODE 0xe9
+#define RELATIVEJUMP_SIZE 5
+#define RELATIVECALL_OPCODE 0xe8
+#define RELATIVE_ADDR_SIZE 4
 #define MAX_INSN_SIZE 16
 #define MAX_STACK_SIZE 64
 #define MIN_STACK_SIZE(ADDR)                                          \
@@ -44,6 +47,17 @@ typedef u8 kprobe_opcode_t;
 
 #define flush_insn_slot(p)     do { } while (0)
 
+/* optinsn template addresses */
+extern kprobe_opcode_t optprobe_template_entry;
+extern kprobe_opcode_t optprobe_template_val;
+extern kprobe_opcode_t optprobe_template_call;
+extern kprobe_opcode_t optprobe_template_end;
+#define MAX_OPTIMIZED_LENGTH (MAX_INSN_SIZE + RELATIVE_ADDR_SIZE)
+#define MAX_OPTINSN_SIZE                               \
+       (((unsigned long)&optprobe_template_end -       \
+         (unsigned long)&optprobe_template_entry) +    \
+        MAX_OPTIMIZED_LENGTH + RELATIVEJUMP_SIZE)
+
 extern const int kretprobe_blacklist_size;
 
 void arch_remove_kprobe(struct kprobe *p);
@@ -64,6 +78,21 @@ struct arch_specific_insn {
        int boostable;
 };
 
+struct arch_optimized_insn {
+       /* copy of the original instructions */
+       kprobe_opcode_t copied_insn[RELATIVE_ADDR_SIZE];
+       /* detour code buffer */
+       kprobe_opcode_t *insn;
+       /* the size of instructions copied to detour code buffer */
+       size_t size;
+};
+
+/* Return true (!0) if optinsn is prepared for optimization. */
+static inline int arch_prepared_optinsn(struct arch_optimized_insn *optinsn)
+{
+       return optinsn->size;
+}
+
 struct prev_kprobe {
        struct kprobe *kp;
        unsigned long status;
index 7c18e1230f5490f1f7a58bd103302ffcb8bf20b1..7a6f54fa13ba426d4cca30862cc38e5e8e4a53cc 100644 (file)
@@ -54,13 +54,23 @@ struct x86_emulate_ctxt;
 struct x86_emulate_ops {
        /*
         * read_std: Read bytes of standard (non-emulated/special) memory.
-        *           Used for instruction fetch, stack operations, and others.
+        *           Used for descriptor reading.
         *  @addr:  [IN ] Linear address from which to read.
         *  @val:   [OUT] Value read from memory, zero-extended to 'u_long'.
         *  @bytes: [IN ] Number of bytes to read from memory.
         */
        int (*read_std)(unsigned long addr, void *val,
-                       unsigned int bytes, struct kvm_vcpu *vcpu);
+                       unsigned int bytes, struct kvm_vcpu *vcpu, u32 *error);
+
+       /*
+        * fetch: Read bytes of standard (non-emulated/special) memory.
+        *        Used for instruction fetch.
+        *  @addr:  [IN ] Linear address from which to read.
+        *  @val:   [OUT] Value read from memory, zero-extended to 'u_long'.
+        *  @bytes: [IN ] Number of bytes to read from memory.
+        */
+       int (*fetch)(unsigned long addr, void *val,
+                       unsigned int bytes, struct kvm_vcpu *vcpu, u32 *error);
 
        /*
         * read_emulated: Read bytes from emulated/special memory area.
@@ -74,7 +84,7 @@ struct x86_emulate_ops {
                             struct kvm_vcpu *vcpu);
 
        /*
-        * write_emulated: Read bytes from emulated/special memory area.
+        * write_emulated: Write bytes to emulated/special memory area.
         *  @addr:  [IN ] Linear address to which to write.
         *  @val:   [IN ] Value to write to memory (low-order bytes used as
         *                required).
@@ -168,6 +178,7 @@ struct x86_emulate_ctxt {
 
 /* Execution mode, passed to the emulator. */
 #define X86EMUL_MODE_REAL     0        /* Real mode.             */
+#define X86EMUL_MODE_VM86     1        /* Virtual 8086 mode.     */
 #define X86EMUL_MODE_PROT16   2        /* 16-bit protected mode. */
 #define X86EMUL_MODE_PROT32   4        /* 32-bit protected mode. */
 #define X86EMUL_MODE_PROT64   8        /* 64-bit (long) mode.    */
index 4f865e8b854096565f5644710b44a6765ce242cc..06d9e79ca37dccad56acb3050ee837c8156ff414 100644 (file)
@@ -25,7 +25,7 @@
 #include <asm/mtrr.h>
 #include <asm/msr-index.h>
 
-#define KVM_MAX_VCPUS 16
+#define KVM_MAX_VCPUS 64
 #define KVM_MEMORY_SLOTS 32
 /* memory slots that does not exposed to userspace */
 #define KVM_PRIVATE_MEM_SLOTS 4
 #define CR3_L_MODE_RESERVED_BITS (CR3_NONPAE_RESERVED_BITS |   \
                                  0xFFFFFF0000000000ULL)
 
-#define KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST                          \
-       (X86_CR0_WP | X86_CR0_NE | X86_CR0_NW | X86_CR0_CD)
-#define KVM_GUEST_CR0_MASK                                             \
-       (KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE)
-#define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST                                \
-       (X86_CR0_WP | X86_CR0_NE | X86_CR0_TS | X86_CR0_MP)
-#define KVM_VM_CR0_ALWAYS_ON                                           \
-       (KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE)
-#define KVM_GUEST_CR4_MASK                                             \
-       (X86_CR4_VME | X86_CR4_PSE | X86_CR4_PAE | X86_CR4_PGE | X86_CR4_VMXE)
-#define KVM_PMODE_VM_CR4_ALWAYS_ON (X86_CR4_PAE | X86_CR4_VMXE)
-#define KVM_RMODE_VM_CR4_ALWAYS_ON (X86_CR4_VME | X86_CR4_PAE | X86_CR4_VMXE)
-
 #define INVALID_PAGE (~(hpa_t)0)
 #define UNMAPPED_GVA (~(gpa_t)0)
 
@@ -256,7 +243,8 @@ struct kvm_mmu {
        void (*new_cr3)(struct kvm_vcpu *vcpu);
        int (*page_fault)(struct kvm_vcpu *vcpu, gva_t gva, u32 err);
        void (*free)(struct kvm_vcpu *vcpu);
-       gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva);
+       gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva, u32 access,
+                           u32 *error);
        void (*prefetch_page)(struct kvm_vcpu *vcpu,
                              struct kvm_mmu_page *page);
        int (*sync_page)(struct kvm_vcpu *vcpu,
@@ -282,13 +270,15 @@ struct kvm_vcpu_arch {
        u32 regs_dirty;
 
        unsigned long cr0;
+       unsigned long cr0_guest_owned_bits;
        unsigned long cr2;
        unsigned long cr3;
        unsigned long cr4;
+       unsigned long cr4_guest_owned_bits;
        unsigned long cr8;
        u32 hflags;
        u64 pdptrs[4]; /* pae */
-       u64 shadow_efer;
+       u64 efer;
        u64 apic_base;
        struct kvm_lapic *apic;    /* kernel irqchip context */
        int32_t apic_arb_prio;
@@ -374,17 +364,27 @@ struct kvm_vcpu_arch {
        /* used for guest single stepping over the given code position */
        u16 singlestep_cs;
        unsigned long singlestep_rip;
+       /* fields used by HYPER-V emulation */
+       u64 hv_vapic;
 };
 
 struct kvm_mem_alias {
        gfn_t base_gfn;
        unsigned long npages;
        gfn_t target_gfn;
+#define KVM_ALIAS_INVALID     1UL
+       unsigned long flags;
 };
 
-struct kvm_arch{
-       int naliases;
+#define KVM_ARCH_HAS_UNALIAS_INSTANTIATION
+
+struct kvm_mem_aliases {
        struct kvm_mem_alias aliases[KVM_ALIAS_SLOTS];
+       int naliases;
+};
+
+struct kvm_arch {
+       struct kvm_mem_aliases *aliases;
 
        unsigned int n_free_mmu_pages;
        unsigned int n_requested_mmu_pages;
@@ -416,6 +416,10 @@ struct kvm_arch{
        s64 kvmclock_offset;
 
        struct kvm_xen_hvm_config xen_hvm_config;
+
+       /* fields used by HYPER-V emulation */
+       u64 hv_guest_os_id;
+       u64 hv_hypercall;
 };
 
 struct kvm_vm_stat {
@@ -471,6 +475,7 @@ struct kvm_x86_ops {
        int (*hardware_setup)(void);               /* __init */
        void (*hardware_unsetup)(void);            /* __exit */
        bool (*cpu_has_accelerated_tpr)(void);
+       void (*cpuid_update)(struct kvm_vcpu *vcpu);
 
        /* Create, but do not attach this VCPU */
        struct kvm_vcpu *(*vcpu_create)(struct kvm *kvm, unsigned id);
@@ -492,6 +497,7 @@ struct kvm_x86_ops {
        void (*set_segment)(struct kvm_vcpu *vcpu,
                            struct kvm_segment *var, int seg);
        void (*get_cs_db_l_bits)(struct kvm_vcpu *vcpu, int *db, int *l);
+       void (*decache_cr0_guest_bits)(struct kvm_vcpu *vcpu);
        void (*decache_cr4_guest_bits)(struct kvm_vcpu *vcpu);
        void (*set_cr0)(struct kvm_vcpu *vcpu, unsigned long cr0);
        void (*set_cr3)(struct kvm_vcpu *vcpu, unsigned long cr3);
@@ -501,12 +507,13 @@ struct kvm_x86_ops {
        void (*set_idt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
        void (*get_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
        void (*set_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
-       unsigned long (*get_dr)(struct kvm_vcpu *vcpu, int dr);
-       void (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value,
-                      int *exception);
+       int (*get_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long *dest);
+       int (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value);
        void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg);
        unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
        void (*set_rflags)(struct kvm_vcpu *vcpu, unsigned long rflags);
+       void (*fpu_activate)(struct kvm_vcpu *vcpu);
+       void (*fpu_deactivate)(struct kvm_vcpu *vcpu);
 
        void (*tlb_flush)(struct kvm_vcpu *vcpu);
 
@@ -531,7 +538,8 @@ struct kvm_x86_ops {
        int (*set_tss_addr)(struct kvm *kvm, unsigned int addr);
        int (*get_tdp_level)(void);
        u64 (*get_mt_mask)(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio);
-       bool (*gb_page_enable)(void);
+       int (*get_lpage_level)(void);
+       bool (*rdtscp_supported)(void);
 
        const struct trace_print_flags *exit_reasons_str;
 };
@@ -606,8 +614,7 @@ int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr,
                    unsigned long value);
 
 void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
-int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
-                               int type_bits, int seg);
+int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg);
 
 int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason);
 
@@ -653,6 +660,10 @@ void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
 int kvm_mmu_load(struct kvm_vcpu *vcpu);
 void kvm_mmu_unload(struct kvm_vcpu *vcpu);
 void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu);
+gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
+gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
+gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
+gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, u32 *error);
 
 int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
 
@@ -666,6 +677,7 @@ void kvm_disable_tdp(void);
 
 int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3);
 int complete_pio(struct kvm_vcpu *vcpu);
+bool kvm_check_iopl(struct kvm_vcpu *vcpu);
 
 struct kvm_memory_slot *gfn_to_memslot_unaliased(struct kvm *kvm, gfn_t gfn);
 
index c584076a47f48acf8d77d57b5a610abf4e366bfa..ffae1420e7d76d0b549360cec1b3b93071bc867a 100644 (file)
@@ -2,6 +2,7 @@
 #define _ASM_X86_KVM_PARA_H
 
 #include <linux/types.h>
+#include <asm/hyperv.h>
 
 /* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx.  It
  * should be used to determine that a VM is running under KVM.
index 1fecb7e6113038f52c2f51f8fde2ef419d40c6e7..38638cd2fa4c87997dec931e0bcdd4f49f6fb28a 100644 (file)
@@ -313,7 +313,7 @@ struct __attribute__ ((__packed__)) vmcb {
 
 #define SVM_EXIT_ERR           -1
 
-#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */
+#define SVM_CR0_SELECTIVE_MASK (X86_CR0_TS | X86_CR0_MP)
 
 #define SVM_VMLOAD ".byte 0x0f, 0x01, 0xda"
 #define SVM_VMRUN  ".byte 0x0f, 0x01, 0xd8"
index 2b4945419a84bd16681c7b4d579ae507ccc8ba96..fb9a080740ecf48970e9648c85caac34bfaf09c6 100644 (file)
@@ -53,6 +53,7 @@
  */
 #define SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES 0x00000001
 #define SECONDARY_EXEC_ENABLE_EPT               0x00000002
+#define SECONDARY_EXEC_RDTSCP                  0x00000008
 #define SECONDARY_EXEC_ENABLE_VPID              0x00000020
 #define SECONDARY_EXEC_WBINVD_EXITING          0x00000040
 #define SECONDARY_EXEC_UNRESTRICTED_GUEST      0x00000080
@@ -251,6 +252,7 @@ enum vmcs_field {
 #define EXIT_REASON_MSR_READ            31
 #define EXIT_REASON_MSR_WRITE           32
 #define EXIT_REASON_MWAIT_INSTRUCTION   36
+#define EXIT_REASON_MONITOR_INSTRUCTION 39
 #define EXIT_REASON_PAUSE_INSTRUCTION   40
 #define EXIT_REASON_MCE_DURING_VMENTRY  41
 #define EXIT_REASON_TPR_BELOW_THRESHOLD 43
@@ -362,6 +364,7 @@ enum vmcs_field {
 #define VMX_EPTP_UC_BIT                                (1ull << 8)
 #define VMX_EPTP_WB_BIT                                (1ull << 14)
 #define VMX_EPT_2MB_PAGE_BIT                   (1ull << 16)
+#define VMX_EPT_1GB_PAGE_BIT                   (1ull << 17)
 #define VMX_EPT_EXTENT_INDIVIDUAL_BIT          (1ull << 24)
 #define VMX_EPT_EXTENT_CONTEXT_BIT             (1ull << 25)
 #define VMX_EPT_EXTENT_GLOBAL_BIT              (1ull << 26)
@@ -374,7 +377,7 @@ enum vmcs_field {
 #define VMX_EPT_READABLE_MASK                  0x1ull
 #define VMX_EPT_WRITABLE_MASK                  0x2ull
 #define VMX_EPT_EXECUTABLE_MASK                        0x4ull
-#define VMX_EPT_IGMT_BIT                       (1ull << 6)
+#define VMX_EPT_IPAT_BIT                       (1ull << 6)
 
 #define VMX_EPT_IDENTITY_PAGETABLE_ADDR                0xfffbc000ul
 
index e6ea0342c8f86e424ae164a479c5b3ffd411e867..3a4bf35c179b7e54b8d1902d22d734cdc4cc4b89 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/memory.h>
+#include <linux/stop_machine.h>
 #include <asm/alternative.h>
 #include <asm/sections.h>
 #include <asm/pgtable.h>
@@ -572,3 +573,62 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
        local_irq_restore(flags);
        return addr;
 }
+
+/*
+ * Cross-modifying kernel text with stop_machine().
+ * This code originally comes from immediate value.
+ */
+static atomic_t stop_machine_first;
+static int wrote_text;
+
+struct text_poke_params {
+       void *addr;
+       const void *opcode;
+       size_t len;
+};
+
+static int __kprobes stop_machine_text_poke(void *data)
+{
+       struct text_poke_params *tpp = data;
+
+       if (atomic_dec_and_test(&stop_machine_first)) {
+               text_poke(tpp->addr, tpp->opcode, tpp->len);
+               smp_wmb();      /* Make sure other cpus see that this has run */
+               wrote_text = 1;
+       } else {
+               while (!wrote_text)
+                       cpu_relax();
+               smp_mb();       /* Load wrote_text before following execution */
+       }
+
+       flush_icache_range((unsigned long)tpp->addr,
+                          (unsigned long)tpp->addr + tpp->len);
+       return 0;
+}
+
+/**
+ * text_poke_smp - Update instructions on a live kernel on SMP
+ * @addr: address to modify
+ * @opcode: source of the copy
+ * @len: length to copy
+ *
+ * Modify multi-byte instruction by using stop_machine() on SMP. This allows
+ * user to poke/set multi-byte text on SMP. Only non-NMI/MCE code modifying
+ * should be allowed, since stop_machine() does _not_ protect code against
+ * NMI and MCE.
+ *
+ * Note: Must be called under get_online_cpus() and text_mutex.
+ */
+void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len)
+{
+       struct text_poke_params tpp;
+
+       tpp.addr = addr;
+       tpp.opcode = opcode;
+       tpp.len = len;
+       atomic_set(&stop_machine_first, 1);
+       wrote_text = 0;
+       stop_machine(stop_machine_text_poke, (void *)&tpp, NULL);
+       return addr;
+}
+
index f138c6c389b921628575575f6a2b0c115441f6c0..870e6cc6ad28bc5d69b292a228992c0f448bb66c 100644 (file)
@@ -10,6 +10,20 @@ if CPU_FREQ
 
 comment "CPUFreq processor drivers"
 
+config X86_PCC_CPUFREQ
+       tristate "Processor Clocking Control interface driver"
+       depends on ACPI && ACPI_PROCESSOR
+       help
+         This driver adds support for the PCC interface.
+
+         For details, take a look at:
+         <file:Documentation/cpu-freq/pcc-cpufreq.txt>.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pcc-cpufreq.
+
+         If in doubt, say N.
+
 config X86_ACPI_CPUFREQ
        tristate "ACPI Processor P-States driver"
        select CPU_FREQ_TABLE
index 509296df294d9432ed6459f8c6b3cccbbb04e1ef..1840c0a5170bfda11e0f80d50f5ba301774e9d1b 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_X86_POWERNOW_K8)          += powernow-k8.o
 obj-$(CONFIG_X86_ACPI_CPUFREQ)         += acpi-cpufreq.o
+obj-$(CONFIG_X86_PCC_CPUFREQ)          += pcc-cpufreq.o
 obj-$(CONFIG_X86_POWERNOW_K6)          += powernow-k6.o
 obj-$(CONFIG_X86_POWERNOW_K7)          += powernow-k7.o
 obj-$(CONFIG_X86_LONGHAUL)             += longhaul.o
diff --git a/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c
new file mode 100644 (file)
index 0000000..ff36d29
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ *  pcc-cpufreq.c - Processor Clocking Control firmware cpufreq interface
+ *
+ *  Copyright (C) 2009 Red Hat, Matthew Garrett <mjg@redhat.com>
+ *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *     Nagananda Chumbalkar <nagananda.chumbalkar@hp.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; 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, GOOD TITLE or NON
+ *  INFRINGEMENT. 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/compiler.h>
+
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include <acpi/processor.h>
+
+#define PCC_VERSION    "1.00.00"
+#define POLL_LOOPS     300
+
+#define CMD_COMPLETE   0x1
+#define CMD_GET_FREQ   0x0
+#define CMD_SET_FREQ   0x1
+
+#define BUF_SZ         4
+
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER,     \
+                                            "pcc-cpufreq", msg)
+
+struct pcc_register_resource {
+       u8 descriptor;
+       u16 length;
+       u8 space_id;
+       u8 bit_width;
+       u8 bit_offset;
+       u8 access_size;
+       u64 address;
+} __attribute__ ((packed));
+
+struct pcc_memory_resource {
+       u8 descriptor;
+       u16 length;
+       u8 space_id;
+       u8 resource_usage;
+       u8 type_specific;
+       u64 granularity;
+       u64 minimum;
+       u64 maximum;
+       u64 translation_offset;
+       u64 address_length;
+} __attribute__ ((packed));
+
+static struct cpufreq_driver pcc_cpufreq_driver;
+
+struct pcc_header {
+       u32 signature;
+       u16 length;
+       u8 major;
+       u8 minor;
+       u32 features;
+       u16 command;
+       u16 status;
+       u32 latency;
+       u32 minimum_time;
+       u32 maximum_time;
+       u32 nominal;
+       u32 throttled_frequency;
+       u32 minimum_frequency;
+};
+
+static void __iomem *pcch_virt_addr;
+static struct pcc_header __iomem *pcch_hdr;
+
+static DEFINE_SPINLOCK(pcc_lock);
+
+static struct acpi_generic_address doorbell;
+
+static u64 doorbell_preserve;
+static u64 doorbell_write;
+
+static u8 OSC_UUID[16] = {0x63, 0x9B, 0x2C, 0x9F, 0x70, 0x91, 0x49, 0x1f,
+                         0xBB, 0x4F, 0xA5, 0x98, 0x2F, 0xA1, 0xB5, 0x46};
+
+struct pcc_cpu {
+       u32 input_offset;
+       u32 output_offset;
+};
+
+static struct pcc_cpu *pcc_cpu_info;
+
+static int pcc_cpufreq_verify(struct cpufreq_policy *policy)
+{
+       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+                                    policy->cpuinfo.max_freq);
+       return 0;
+}
+
+static inline void pcc_cmd(void)
+{
+       u64 doorbell_value;
+       int i;
+
+       acpi_read(&doorbell_value, &doorbell);
+       acpi_write((doorbell_value & doorbell_preserve) | doorbell_write,
+                  &doorbell);
+
+       for (i = 0; i < POLL_LOOPS; i++) {
+               if (ioread16(&pcch_hdr->status) & CMD_COMPLETE)
+                       break;
+       }
+}
+
+static inline void pcc_clear_mapping(void)
+{
+       if (pcch_virt_addr)
+               iounmap(pcch_virt_addr);
+       pcch_virt_addr = NULL;
+}
+
+static unsigned int pcc_get_freq(unsigned int cpu)
+{
+       struct pcc_cpu *pcc_cpu_data;
+       unsigned int curr_freq;
+       unsigned int freq_limit;
+       u16 status;
+       u32 input_buffer;
+       u32 output_buffer;
+
+       spin_lock(&pcc_lock);
+
+       dprintk("get: get_freq for CPU %d\n", cpu);
+       pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+
+       input_buffer = 0x1;
+       iowrite32(input_buffer,
+                       (pcch_virt_addr + pcc_cpu_data->input_offset));
+       iowrite16(CMD_GET_FREQ, &pcch_hdr->command);
+
+       pcc_cmd();
+
+       output_buffer =
+               ioread32(pcch_virt_addr + pcc_cpu_data->output_offset);
+
+       /* Clear the input buffer - we are done with the current command */
+       memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
+
+       status = ioread16(&pcch_hdr->status);
+       if (status != CMD_COMPLETE) {
+               dprintk("get: FAILED: for CPU %d, status is %d\n",
+                       cpu, status);
+               goto cmd_incomplete;
+       }
+       iowrite16(0, &pcch_hdr->status);
+       curr_freq = (((ioread32(&pcch_hdr->nominal) * (output_buffer & 0xff))
+                       / 100) * 1000);
+
+       dprintk("get: SUCCESS: (virtual) output_offset for cpu %d is "
+               "0x%x, contains a value of: 0x%x. Speed is: %d MHz\n",
+               cpu, (pcch_virt_addr + pcc_cpu_data->output_offset),
+               output_buffer, curr_freq);
+
+       freq_limit = (output_buffer >> 8) & 0xff;
+       if (freq_limit != 0xff) {
+               dprintk("get: frequency for cpu %d is being temporarily"
+                       " capped at %d\n", cpu, curr_freq);
+       }
+
+       spin_unlock(&pcc_lock);
+       return curr_freq;
+
+cmd_incomplete:
+       iowrite16(0, &pcch_hdr->status);
+       spin_unlock(&pcc_lock);
+       return -EINVAL;
+}
+
+static int pcc_cpufreq_target(struct cpufreq_policy *policy,
+                             unsigned int target_freq,
+                             unsigned int relation)
+{
+       struct pcc_cpu *pcc_cpu_data;
+       struct cpufreq_freqs freqs;
+       u16 status;
+       u32 input_buffer;
+       int cpu;
+
+       spin_lock(&pcc_lock);
+       cpu = policy->cpu;
+       pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+
+       dprintk("target: CPU %d should go to target freq: %d "
+               "(virtual) input_offset is 0x%x\n",
+               cpu, target_freq,
+               (pcch_virt_addr + pcc_cpu_data->input_offset));
+
+       freqs.new = target_freq;
+       freqs.cpu = cpu;
+       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+       input_buffer = 0x1 | (((target_freq * 100)
+                              / (ioread32(&pcch_hdr->nominal) * 1000)) << 8);
+       iowrite32(input_buffer,
+                       (pcch_virt_addr + pcc_cpu_data->input_offset));
+       iowrite16(CMD_SET_FREQ, &pcch_hdr->command);
+
+       pcc_cmd();
+
+       /* Clear the input buffer - we are done with the current command */
+       memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
+
+       status = ioread16(&pcch_hdr->status);
+       if (status != CMD_COMPLETE) {
+               dprintk("target: FAILED for cpu %d, with status: 0x%x\n",
+                       cpu, status);
+               goto cmd_incomplete;
+       }
+       iowrite16(0, &pcch_hdr->status);
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+       dprintk("target: was SUCCESSFUL for cpu %d\n", cpu);
+       spin_unlock(&pcc_lock);
+
+       return 0;
+
+cmd_incomplete:
+       iowrite16(0, &pcch_hdr->status);
+       spin_unlock(&pcc_lock);
+       return -EINVAL;
+}
+
+static int pcc_get_offset(int cpu)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *pccp, *offset;
+       struct pcc_cpu *pcc_cpu_data;
+       struct acpi_processor *pr;
+       int ret = 0;
+
+       pr = per_cpu(processors, cpu);
+       pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+
+       status = acpi_evaluate_object(pr->handle, "PCCP", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       pccp = buffer.pointer;
+       if (!pccp || pccp->type != ACPI_TYPE_PACKAGE) {
+               ret = -ENODEV;
+               goto out_free;
+       };
+
+       offset = &(pccp->package.elements[0]);
+       if (!offset || offset->type != ACPI_TYPE_INTEGER) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       pcc_cpu_data->input_offset = offset->integer.value;
+
+       offset = &(pccp->package.elements[1]);
+       if (!offset || offset->type != ACPI_TYPE_INTEGER) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       pcc_cpu_data->output_offset = offset->integer.value;
+
+       memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
+       memset_io((pcch_virt_addr + pcc_cpu_data->output_offset), 0, BUF_SZ);
+
+       dprintk("pcc_get_offset: for CPU %d: pcc_cpu_data "
+               "input_offset: 0x%x, pcc_cpu_data output_offset: 0x%x\n",
+               cpu, pcc_cpu_data->input_offset, pcc_cpu_data->output_offset);
+out_free:
+       kfree(buffer.pointer);
+       return ret;
+}
+
+static int __init pcc_cpufreq_do_osc(acpi_handle *handle)
+{
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object in_params[4];
+       union acpi_object *out_obj;
+       u32 capabilities[2];
+       u32 errors;
+       u32 supported;
+       int ret = 0;
+
+       input.count = 4;
+       input.pointer = in_params;
+       input.count = 4;
+       input.pointer = in_params;
+       in_params[0].type               = ACPI_TYPE_BUFFER;
+       in_params[0].buffer.length      = 16;
+       in_params[0].buffer.pointer     = OSC_UUID;
+       in_params[1].type               = ACPI_TYPE_INTEGER;
+       in_params[1].integer.value      = 1;
+       in_params[2].type               = ACPI_TYPE_INTEGER;
+       in_params[2].integer.value      = 2;
+       in_params[3].type               = ACPI_TYPE_BUFFER;
+       in_params[3].buffer.length      = 8;
+       in_params[3].buffer.pointer     = (u8 *)&capabilities;
+
+       capabilities[0] = OSC_QUERY_ENABLE;
+       capabilities[1] = 0x1;
+
+       status = acpi_evaluate_object(*handle, "_OSC", &input, &output);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       if (!output.length)
+               return -ENODEV;
+
+       out_obj = output.pointer;
+       if (out_obj->type != ACPI_TYPE_BUFFER) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
+       if (errors) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       supported = *((u32 *)(out_obj->buffer.pointer + 4));
+       if (!(supported & 0x1)) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       kfree(output.pointer);
+       capabilities[0] = 0x0;
+       capabilities[1] = 0x1;
+
+       status = acpi_evaluate_object(*handle, "_OSC", &input, &output);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       if (!output.length)
+               return -ENODEV;
+
+       out_obj = output.pointer;
+       if (out_obj->type != ACPI_TYPE_BUFFER) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
+       if (errors) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       supported = *((u32 *)(out_obj->buffer.pointer + 4));
+       if (!(supported & 0x1)) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+out_free:
+       kfree(output.pointer);
+       return ret;
+}
+
+static int __init pcc_cpufreq_probe(void)
+{
+       acpi_status status;
+       struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct pcc_memory_resource *mem_resource;
+       struct pcc_register_resource *reg_resource;
+       union acpi_object *out_obj, *member;
+       acpi_handle handle, osc_handle;
+       int ret = 0;
+
+       status = acpi_get_handle(NULL, "\\_SB", &handle);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       status = acpi_get_handle(handle, "_OSC", &osc_handle);
+       if (ACPI_SUCCESS(status)) {
+               ret = pcc_cpufreq_do_osc(&osc_handle);
+               if (ret)
+                       dprintk("probe: _OSC evaluation did not succeed\n");
+               /* Firmware's use of _OSC is optional */
+               ret = 0;
+       }
+
+       status = acpi_evaluate_object(handle, "PCCH", NULL, &output);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       out_obj = output.pointer;
+       if (out_obj->type != ACPI_TYPE_PACKAGE) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       member = &out_obj->package.elements[0];
+       if (member->type != ACPI_TYPE_BUFFER) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       mem_resource = (struct pcc_memory_resource *)member->buffer.pointer;
+
+       dprintk("probe: mem_resource descriptor: 0x%x,"
+               " length: %d, space_id: %d, resource_usage: %d,"
+               " type_specific: %d, granularity: 0x%llx,"
+               " minimum: 0x%llx, maximum: 0x%llx,"
+               " translation_offset: 0x%llx, address_length: 0x%llx\n",
+               mem_resource->descriptor, mem_resource->length,
+               mem_resource->space_id, mem_resource->resource_usage,
+               mem_resource->type_specific, mem_resource->granularity,
+               mem_resource->minimum, mem_resource->maximum,
+               mem_resource->translation_offset,
+               mem_resource->address_length);
+
+       if (mem_resource->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+               ret = -ENODEV;
+               goto out_free;
+       }
+
+       pcch_virt_addr = ioremap_nocache(mem_resource->minimum,
+                                       mem_resource->address_length);
+       if (pcch_virt_addr == NULL) {
+               dprintk("probe: could not map shared mem region\n");
+               goto out_free;
+       }
+       pcch_hdr = pcch_virt_addr;
+
+       dprintk("probe: PCCH header (virtual) addr: 0x%p\n", pcch_hdr);
+       dprintk("probe: PCCH header is at physical address: 0x%llx,"
+               " signature: 0x%x, length: %d bytes, major: %d, minor: %d,"
+               " supported features: 0x%x, command field: 0x%x,"
+               " status field: 0x%x, nominal latency: %d us\n",
+               mem_resource->minimum, ioread32(&pcch_hdr->signature),
+               ioread16(&pcch_hdr->length), ioread8(&pcch_hdr->major),
+               ioread8(&pcch_hdr->minor), ioread32(&pcch_hdr->features),
+               ioread16(&pcch_hdr->command), ioread16(&pcch_hdr->status),
+               ioread32(&pcch_hdr->latency));
+
+       dprintk("probe: min time between commands: %d us,"
+               " max time between commands: %d us,"
+               " nominal CPU frequency: %d MHz,"
+               " minimum CPU frequency: %d MHz,"
+               " minimum CPU frequency without throttling: %d MHz\n",
+               ioread32(&pcch_hdr->minimum_time),
+               ioread32(&pcch_hdr->maximum_time),
+               ioread32(&pcch_hdr->nominal),
+               ioread32(&pcch_hdr->throttled_frequency),
+               ioread32(&pcch_hdr->minimum_frequency));
+
+       member = &out_obj->package.elements[1];
+       if (member->type != ACPI_TYPE_BUFFER) {
+               ret = -ENODEV;
+               goto pcch_free;
+       }
+
+       reg_resource = (struct pcc_register_resource *)member->buffer.pointer;
+
+       doorbell.space_id = reg_resource->space_id;
+       doorbell.bit_width = reg_resource->bit_width;
+       doorbell.bit_offset = reg_resource->bit_offset;
+       doorbell.access_width = 64;
+       doorbell.address = reg_resource->address;
+
+       dprintk("probe: doorbell: space_id is %d, bit_width is %d, "
+               "bit_offset is %d, access_width is %d, address is 0x%llx\n",
+               doorbell.space_id, doorbell.bit_width, doorbell.bit_offset,
+               doorbell.access_width, reg_resource->address);
+
+       member = &out_obj->package.elements[2];
+       if (member->type != ACPI_TYPE_INTEGER) {
+               ret = -ENODEV;
+               goto pcch_free;
+       }
+
+       doorbell_preserve = member->integer.value;
+
+       member = &out_obj->package.elements[3];
+       if (member->type != ACPI_TYPE_INTEGER) {
+               ret = -ENODEV;
+               goto pcch_free;
+       }
+
+       doorbell_write = member->integer.value;
+
+       dprintk("probe: doorbell_preserve: 0x%llx,"
+               " doorbell_write: 0x%llx\n",
+               doorbell_preserve, doorbell_write);
+
+       pcc_cpu_info = alloc_percpu(struct pcc_cpu);
+       if (!pcc_cpu_info) {
+               ret = -ENOMEM;
+               goto pcch_free;
+       }
+
+       printk(KERN_DEBUG "pcc-cpufreq: (v%s) driver loaded with frequency"
+              " limits: %d MHz, %d MHz\n", PCC_VERSION,
+              ioread32(&pcch_hdr->minimum_frequency),
+              ioread32(&pcch_hdr->nominal));
+       kfree(output.pointer);
+       return ret;
+pcch_free:
+       pcc_clear_mapping();
+out_free:
+       kfree(output.pointer);
+       return ret;
+}
+
+static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+       unsigned int cpu = policy->cpu;
+       unsigned int result = 0;
+
+       if (!pcch_virt_addr) {
+               result = -1;
+               goto pcch_null;
+       }
+
+       result = pcc_get_offset(cpu);
+       if (result) {
+               dprintk("init: PCCP evaluation failed\n");
+               goto free;
+       }
+
+       policy->max = policy->cpuinfo.max_freq =
+               ioread32(&pcch_hdr->nominal) * 1000;
+       policy->min = policy->cpuinfo.min_freq =
+               ioread32(&pcch_hdr->minimum_frequency) * 1000;
+       policy->cur = pcc_get_freq(cpu);
+
+       dprintk("init: policy->max is %d, policy->min is %d\n",
+               policy->max, policy->min);
+
+       return 0;
+free:
+       pcc_clear_mapping();
+       free_percpu(pcc_cpu_info);
+pcch_null:
+       return result;
+}
+
+static int pcc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+       return 0;
+}
+
+static struct cpufreq_driver pcc_cpufreq_driver = {
+       .flags = CPUFREQ_CONST_LOOPS,
+       .get = pcc_get_freq,
+       .verify = pcc_cpufreq_verify,
+       .target = pcc_cpufreq_target,
+       .init = pcc_cpufreq_cpu_init,
+       .exit = pcc_cpufreq_cpu_exit,
+       .name = "pcc-cpufreq",
+       .owner = THIS_MODULE,
+};
+
+static int __init pcc_cpufreq_init(void)
+{
+       int ret;
+
+       if (acpi_disabled)
+               return 0;
+
+       ret = pcc_cpufreq_probe();
+       if (ret) {
+               dprintk("pcc_cpufreq_init: PCCH evaluation failed\n");
+               return ret;
+       }
+
+       ret = cpufreq_register_driver(&pcc_cpufreq_driver);
+
+       return ret;
+}
+
+static void __exit pcc_cpufreq_exit(void)
+{
+       cpufreq_unregister_driver(&pcc_cpufreq_driver);
+
+       pcc_clear_mapping();
+
+       free_percpu(pcc_cpu_info);
+}
+
+MODULE_AUTHOR("Matthew Garrett, Naga Chumbalkar");
+MODULE_VERSION(PCC_VERSION);
+MODULE_DESCRIPTION("Processor Clocking Control interface driver");
+MODULE_LICENSE("GPL");
+
+late_initcall(pcc_cpufreq_init);
+module_exit(pcc_cpufreq_exit);
index fe4622e8c837ec7be504b84efb52da9ae0ca82ae..79556bd9b602a41b3ef3bc5502eccb05ed8dba1d 100644 (file)
@@ -145,6 +145,7 @@ struct set_mtrr_data {
 
 /**
  * ipi_handler - Synchronisation handler. Executed by "other" CPUs.
+ * @info: pointer to mtrr configuration data
  *
  * Returns nothing.
  */
index 641ccb9dddbc9d2cce6fad3065796b447ec950e9..b1fbdeecf6c923e9f3bcf93e487a89abd7016dac 100644 (file)
@@ -676,7 +676,7 @@ static int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
                        if (c->weight != w)
                                continue;
 
-                       for_each_bit(j, c->idxmsk, X86_PMC_IDX_MAX) {
+                       for_each_set_bit(j, c->idxmsk, X86_PMC_IDX_MAX) {
                                if (!test_bit(j, used_mask))
                                        break;
                        }
index cf6590cf4a5f8fe5270efc40bfaa8646704d389a..977e7544738c5d657c50370856a94569b6bc0df2 100644 (file)
@@ -757,7 +757,7 @@ again:
 
        inc_irq_stat(apic_perf_irqs);
        ack = status;
-       for_each_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
+       for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
                struct perf_event *event = cpuc->events[bit];
 
                clear_bit(bit, (unsigned long *) &status);
index 5de9f4a9c3fd0e77edc96ed1ed46b055d7a18bdc..b43bbaebe2c0884d009c4d07a30f2db64f6538b5 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/module.h>
 #include <linux/kdebug.h>
 #include <linux/kallsyms.h>
+#include <linux/ftrace.h>
 
 #include <asm/cacheflush.h>
 #include <asm/desc.h>
@@ -106,16 +107,22 @@ struct kretprobe_blackpoint kretprobe_blacklist[] = {
 };
 const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
 
-/* Insert a jump instruction at address 'from', which jumps to address 'to'.*/
-static void __kprobes set_jmp_op(void *from, void *to)
+static void __kprobes __synthesize_relative_insn(void *from, void *to, u8 op)
 {
-       struct __arch_jmp_op {
-               char op;
+       struct __arch_relative_insn {
+               u8 op;
                s32 raddr;
-       } __attribute__((packed)) * jop;
-       jop = (struct __arch_jmp_op *)from;
-       jop->raddr = (s32)((long)(to) - ((long)(from) + 5));
-       jop->op = RELATIVEJUMP_INSTRUCTION;
+       } __attribute__((packed)) *insn;
+
+       insn = (struct __arch_relative_insn *)from;
+       insn->raddr = (s32)((long)(to) - ((long)(from) + 5));
+       insn->op = op;
+}
+
+/* Insert a jump instruction at address 'from', which jumps to address 'to'.*/
+static void __kprobes synthesize_reljump(void *from, void *to)
+{
+       __synthesize_relative_insn(from, to, RELATIVEJUMP_OPCODE);
 }
 
 /*
@@ -202,7 +209,7 @@ static int recover_probed_instruction(kprobe_opcode_t *buf, unsigned long addr)
        /*
         *  Basically, kp->ainsn.insn has an original instruction.
         *  However, RIP-relative instruction can not do single-stepping
-        *  at different place, fix_riprel() tweaks the displacement of
+        *  at different place, __copy_instruction() tweaks the displacement of
         *  that instruction. In that case, we can't recover the instruction
         *  from the kp->ainsn.insn.
         *
@@ -284,21 +291,37 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
 }
 
 /*
- * Adjust the displacement if the instruction uses the %rip-relative
- * addressing mode.
+ * Copy an instruction and adjust the displacement if the instruction
+ * uses the %rip-relative addressing mode.
  * If it does, Return the address of the 32-bit displacement word.
  * If not, return null.
  * Only applicable to 64-bit x86.
  */
-static void __kprobes fix_riprel(struct kprobe *p)
+static int __kprobes __copy_instruction(u8 *dest, u8 *src, int recover)
 {
-#ifdef CONFIG_X86_64
        struct insn insn;
-       kernel_insn_init(&insn, p->ainsn.insn);
+       int ret;
+       kprobe_opcode_t buf[MAX_INSN_SIZE];
 
+       kernel_insn_init(&insn, src);
+       if (recover) {
+               insn_get_opcode(&insn);
+               if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) {
+                       ret = recover_probed_instruction(buf,
+                                                        (unsigned long)src);
+                       if (ret)
+                               return 0;
+                       kernel_insn_init(&insn, buf);
+               }
+       }
+       insn_get_length(&insn);
+       memcpy(dest, insn.kaddr, insn.length);
+
+#ifdef CONFIG_X86_64
        if (insn_rip_relative(&insn)) {
                s64 newdisp;
                u8 *disp;
+               kernel_insn_init(&insn, dest);
                insn_get_displacement(&insn);
                /*
                 * The copied instruction uses the %rip-relative addressing
@@ -312,20 +335,23 @@ static void __kprobes fix_riprel(struct kprobe *p)
                 * extension of the original signed 32-bit displacement would
                 * have given.
                 */
-               newdisp = (u8 *) p->addr + (s64) insn.displacement.value -
-                         (u8 *) p->ainsn.insn;
+               newdisp = (u8 *) src + (s64) insn.displacement.value -
+                         (u8 *) dest;
                BUG_ON((s64) (s32) newdisp != newdisp); /* Sanity check.  */
-               disp = (u8 *) p->ainsn.insn + insn_offset_displacement(&insn);
+               disp = (u8 *) dest + insn_offset_displacement(&insn);
                *(s32 *) disp = (s32) newdisp;
        }
 #endif
+       return insn.length;
 }
 
 static void __kprobes arch_copy_kprobe(struct kprobe *p)
 {
-       memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
-
-       fix_riprel(p);
+       /*
+        * Copy an instruction without recovering int3, because it will be
+        * put by another subsystem.
+        */
+       __copy_instruction(p->ainsn.insn, p->addr, 0);
 
        if (can_boost(p->addr))
                p->ainsn.boostable = 0;
@@ -406,18 +432,6 @@ static void __kprobes restore_btf(void)
                update_debugctlmsr(current->thread.debugctlmsr);
 }
 
-static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
-{
-       clear_btf();
-       regs->flags |= X86_EFLAGS_TF;
-       regs->flags &= ~X86_EFLAGS_IF;
-       /* single step inline if the instruction is an int3 */
-       if (p->opcode == BREAKPOINT_INSTRUCTION)
-               regs->ip = (unsigned long)p->addr;
-       else
-               regs->ip = (unsigned long)p->ainsn.insn;
-}
-
 void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
                                      struct pt_regs *regs)
 {
@@ -429,20 +443,50 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
        *sara = (unsigned long) &kretprobe_trampoline;
 }
 
+#ifdef CONFIG_OPTPROBES
+static int  __kprobes setup_detour_execution(struct kprobe *p,
+                                            struct pt_regs *regs,
+                                            int reenter);
+#else
+#define setup_detour_execution(p, regs, reenter) (0)
+#endif
+
 static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs,
-                                      struct kprobe_ctlblk *kcb)
+                                      struct kprobe_ctlblk *kcb, int reenter)
 {
+       if (setup_detour_execution(p, regs, reenter))
+               return;
+
 #if !defined(CONFIG_PREEMPT)
        if (p->ainsn.boostable == 1 && !p->post_handler) {
                /* Boost up -- we can execute copied instructions directly */
-               reset_current_kprobe();
+               if (!reenter)
+                       reset_current_kprobe();
+               /*
+                * Reentering boosted probe doesn't reset current_kprobe,
+                * nor set current_kprobe, because it doesn't use single
+                * stepping.
+                */
                regs->ip = (unsigned long)p->ainsn.insn;
                preempt_enable_no_resched();
                return;
        }
 #endif
-       prepare_singlestep(p, regs);
-       kcb->kprobe_status = KPROBE_HIT_SS;
+       if (reenter) {
+               save_previous_kprobe(kcb);
+               set_current_kprobe(p, regs, kcb);
+               kcb->kprobe_status = KPROBE_REENTER;
+       } else
+               kcb->kprobe_status = KPROBE_HIT_SS;
+       /* Prepare real single stepping */
+       clear_btf();
+       regs->flags |= X86_EFLAGS_TF;
+       regs->flags &= ~X86_EFLAGS_IF;
+       /* single step inline if the instruction is an int3 */
+       if (p->opcode == BREAKPOINT_INSTRUCTION)
+               regs->ip = (unsigned long)p->addr;
+       else
+               regs->ip = (unsigned long)p->ainsn.insn;
 }
 
 /*
@@ -456,11 +500,8 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
        switch (kcb->kprobe_status) {
        case KPROBE_HIT_SSDONE:
        case KPROBE_HIT_ACTIVE:
-               save_previous_kprobe(kcb);
-               set_current_kprobe(p, regs, kcb);
                kprobes_inc_nmissed_count(p);
-               prepare_singlestep(p, regs);
-               kcb->kprobe_status = KPROBE_REENTER;
+               setup_singlestep(p, regs, kcb, 1);
                break;
        case KPROBE_HIT_SS:
                /* A probe has been hit in the codepath leading up to, or just
@@ -535,13 +576,13 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
                         * more here.
                         */
                        if (!p->pre_handler || !p->pre_handler(p, regs))
-                               setup_singlestep(p, regs, kcb);
+                               setup_singlestep(p, regs, kcb, 0);
                        return 1;
                }
        } else if (kprobe_running()) {
                p = __get_cpu_var(current_kprobe);
                if (p->break_handler && p->break_handler(p, regs)) {
-                       setup_singlestep(p, regs, kcb);
+                       setup_singlestep(p, regs, kcb, 0);
                        return 1;
                }
        } /* else: not a kprobe fault; let the kernel handle it */
@@ -550,6 +591,69 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
        return 0;
 }
 
+#ifdef CONFIG_X86_64
+#define SAVE_REGS_STRING               \
+       /* Skip cs, ip, orig_ax. */     \
+       "       subq $24, %rsp\n"       \
+       "       pushq %rdi\n"           \
+       "       pushq %rsi\n"           \
+       "       pushq %rdx\n"           \
+       "       pushq %rcx\n"           \
+       "       pushq %rax\n"           \
+       "       pushq %r8\n"            \
+       "       pushq %r9\n"            \
+       "       pushq %r10\n"           \
+       "       pushq %r11\n"           \
+       "       pushq %rbx\n"           \
+       "       pushq %rbp\n"           \
+       "       pushq %r12\n"           \
+       "       pushq %r13\n"           \
+       "       pushq %r14\n"           \
+       "       pushq %r15\n"
+#define RESTORE_REGS_STRING            \
+       "       popq %r15\n"            \
+       "       popq %r14\n"            \
+       "       popq %r13\n"            \
+       "       popq %r12\n"            \
+       "       popq %rbp\n"            \
+       "       popq %rbx\n"            \
+       "       popq %r11\n"            \
+       "       popq %r10\n"            \
+       "       popq %r9\n"             \
+       "       popq %r8\n"             \
+       "       popq %rax\n"            \
+       "       popq %rcx\n"            \
+       "       popq %rdx\n"            \
+       "       popq %rsi\n"            \
+       "       popq %rdi\n"            \
+       /* Skip orig_ax, ip, cs */      \
+       "       addq $24, %rsp\n"
+#else
+#define SAVE_REGS_STRING               \
+       /* Skip cs, ip, orig_ax and gs. */      \
+       "       subl $16, %esp\n"       \
+       "       pushl %fs\n"            \
+       "       pushl %ds\n"            \
+       "       pushl %es\n"            \
+       "       pushl %eax\n"           \
+       "       pushl %ebp\n"           \
+       "       pushl %edi\n"           \
+       "       pushl %esi\n"           \
+       "       pushl %edx\n"           \
+       "       pushl %ecx\n"           \
+       "       pushl %ebx\n"
+#define RESTORE_REGS_STRING            \
+       "       popl %ebx\n"            \
+       "       popl %ecx\n"            \
+       "       popl %edx\n"            \
+       "       popl %esi\n"            \
+       "       popl %edi\n"            \
+       "       popl %ebp\n"            \
+       "       popl %eax\n"            \
+       /* Skip ds, es, fs, gs, orig_ax, and ip. Note: don't pop cs here*/\
+       "       addl $24, %esp\n"
+#endif
+
 /*
  * When a retprobed function returns, this code saves registers and
  * calls trampoline_handler() runs, which calls the kretprobe's handler.
@@ -563,65 +667,16 @@ static void __used __kprobes kretprobe_trampoline_holder(void)
                        /* We don't bother saving the ss register */
                        "       pushq %rsp\n"
                        "       pushfq\n"
-                       /*
-                        * Skip cs, ip, orig_ax.
-                        * trampoline_handler() will plug in these values
-                        */
-                       "       subq $24, %rsp\n"
-                       "       pushq %rdi\n"
-                       "       pushq %rsi\n"
-                       "       pushq %rdx\n"
-                       "       pushq %rcx\n"
-                       "       pushq %rax\n"
-                       "       pushq %r8\n"
-                       "       pushq %r9\n"
-                       "       pushq %r10\n"
-                       "       pushq %r11\n"
-                       "       pushq %rbx\n"
-                       "       pushq %rbp\n"
-                       "       pushq %r12\n"
-                       "       pushq %r13\n"
-                       "       pushq %r14\n"
-                       "       pushq %r15\n"
+                       SAVE_REGS_STRING
                        "       movq %rsp, %rdi\n"
                        "       call trampoline_handler\n"
                        /* Replace saved sp with true return address. */
                        "       movq %rax, 152(%rsp)\n"
-                       "       popq %r15\n"
-                       "       popq %r14\n"
-                       "       popq %r13\n"
-                       "       popq %r12\n"
-                       "       popq %rbp\n"
-                       "       popq %rbx\n"
-                       "       popq %r11\n"
-                       "       popq %r10\n"
-                       "       popq %r9\n"
-                       "       popq %r8\n"
-                       "       popq %rax\n"
-                       "       popq %rcx\n"
-                       "       popq %rdx\n"
-                       "       popq %rsi\n"
-                       "       popq %rdi\n"
-                       /* Skip orig_ax, ip, cs */
-                       "       addq $24, %rsp\n"
+                       RESTORE_REGS_STRING
                        "       popfq\n"
 #else
                        "       pushf\n"
-                       /*
-                        * Skip cs, ip, orig_ax and gs.
-                        * trampoline_handler() will plug in these values
-                        */
-                       "       subl $16, %esp\n"
-                       "       pushl %fs\n"
-                       "       pushl %es\n"
-                       "       pushl %ds\n"
-                       "       pushl %eax\n"
-                       "       pushl %ebp\n"
-                       "       pushl %edi\n"
-                       "       pushl %esi\n"
-                       "       pushl %edx\n"
-                       "       pushl %ecx\n"
-                       "       pushl %ebx\n"
+                       SAVE_REGS_STRING
                        "       movl %esp, %eax\n"
                        "       call trampoline_handler\n"
                        /* Move flags to cs */
@@ -629,15 +684,7 @@ static void __used __kprobes kretprobe_trampoline_holder(void)
                        "       movl %edx, 52(%esp)\n"
                        /* Replace saved flags with true return address. */
                        "       movl %eax, 56(%esp)\n"
-                       "       popl %ebx\n"
-                       "       popl %ecx\n"
-                       "       popl %edx\n"
-                       "       popl %esi\n"
-                       "       popl %edi\n"
-                       "       popl %ebp\n"
-                       "       popl %eax\n"
-                       /* Skip ds, es, fs, gs, orig_ax and ip */
-                       "       addl $24, %esp\n"
+                       RESTORE_REGS_STRING
                        "       popf\n"
 #endif
                        "       ret\n");
@@ -805,8 +852,8 @@ static void __kprobes resume_execution(struct kprobe *p,
                         * These instructions can be executed directly if it
                         * jumps back to correct address.
                         */
-                       set_jmp_op((void *)regs->ip,
-                                  (void *)orig_ip + (regs->ip - copy_ip));
+                       synthesize_reljump((void *)regs->ip,
+                               (void *)orig_ip + (regs->ip - copy_ip));
                        p->ainsn.boostable = 1;
                } else {
                        p->ainsn.boostable = -1;
@@ -1033,6 +1080,358 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
        return 0;
 }
 
+
+#ifdef CONFIG_OPTPROBES
+
+/* Insert a call instruction at address 'from', which calls address 'to'.*/
+static void __kprobes synthesize_relcall(void *from, void *to)
+{
+       __synthesize_relative_insn(from, to, RELATIVECALL_OPCODE);
+}
+
+/* Insert a move instruction which sets a pointer to eax/rdi (1st arg). */
+static void __kprobes synthesize_set_arg1(kprobe_opcode_t *addr,
+                                         unsigned long val)
+{
+#ifdef CONFIG_X86_64
+       *addr++ = 0x48;
+       *addr++ = 0xbf;
+#else
+       *addr++ = 0xb8;
+#endif
+       *(unsigned long *)addr = val;
+}
+
+void __kprobes kprobes_optinsn_template_holder(void)
+{
+       asm volatile (
+                       ".global optprobe_template_entry\n"
+                       "optprobe_template_entry: \n"
+#ifdef CONFIG_X86_64
+                       /* We don't bother saving the ss register */
+                       "       pushq %rsp\n"
+                       "       pushfq\n"
+                       SAVE_REGS_STRING
+                       "       movq %rsp, %rsi\n"
+                       ".global optprobe_template_val\n"
+                       "optprobe_template_val: \n"
+                       ASM_NOP5
+                       ASM_NOP5
+                       ".global optprobe_template_call\n"
+                       "optprobe_template_call: \n"
+                       ASM_NOP5
+                       /* Move flags to rsp */
+                       "       movq 144(%rsp), %rdx\n"
+                       "       movq %rdx, 152(%rsp)\n"
+                       RESTORE_REGS_STRING
+                       /* Skip flags entry */
+                       "       addq $8, %rsp\n"
+                       "       popfq\n"
+#else /* CONFIG_X86_32 */
+                       "       pushf\n"
+                       SAVE_REGS_STRING
+                       "       movl %esp, %edx\n"
+                       ".global optprobe_template_val\n"
+                       "optprobe_template_val: \n"
+                       ASM_NOP5
+                       ".global optprobe_template_call\n"
+                       "optprobe_template_call: \n"
+                       ASM_NOP5
+                       RESTORE_REGS_STRING
+                       "       addl $4, %esp\n"        /* skip cs */
+                       "       popf\n"
+#endif
+                       ".global optprobe_template_end\n"
+                       "optprobe_template_end: \n");
+}
+
+#define TMPL_MOVE_IDX \
+       ((long)&optprobe_template_val - (long)&optprobe_template_entry)
+#define TMPL_CALL_IDX \
+       ((long)&optprobe_template_call - (long)&optprobe_template_entry)
+#define TMPL_END_IDX \
+       ((long)&optprobe_template_end - (long)&optprobe_template_entry)
+
+#define INT3_SIZE sizeof(kprobe_opcode_t)
+
+/* Optimized kprobe call back function: called from optinsn */
+static void __kprobes optimized_callback(struct optimized_kprobe *op,
+                                        struct pt_regs *regs)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       preempt_disable();
+       if (kprobe_running()) {
+               kprobes_inc_nmissed_count(&op->kp);
+       } else {
+               /* Save skipped registers */
+#ifdef CONFIG_X86_64
+               regs->cs = __KERNEL_CS;
+#else
+               regs->cs = __KERNEL_CS | get_kernel_rpl();
+               regs->gs = 0;
+#endif
+               regs->ip = (unsigned long)op->kp.addr + INT3_SIZE;
+               regs->orig_ax = ~0UL;
+
+               __get_cpu_var(current_kprobe) = &op->kp;
+               kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+               opt_pre_handler(&op->kp, regs);
+               __get_cpu_var(current_kprobe) = NULL;
+       }
+       preempt_enable_no_resched();
+}
+
+static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src)
+{
+       int len = 0, ret;
+
+       while (len < RELATIVEJUMP_SIZE) {
+               ret = __copy_instruction(dest + len, src + len, 1);
+               if (!ret || !can_boost(dest + len))
+                       return -EINVAL;
+               len += ret;
+       }
+       /* Check whether the address range is reserved */
+       if (ftrace_text_reserved(src, src + len - 1) ||
+           alternatives_text_reserved(src, src + len - 1))
+               return -EBUSY;
+
+       return len;
+}
+
+/* Check whether insn is indirect jump */
+static int __kprobes insn_is_indirect_jump(struct insn *insn)
+{
+       return ((insn->opcode.bytes[0] == 0xff &&
+               (X86_MODRM_REG(insn->modrm.value) & 6) == 4) || /* Jump */
+               insn->opcode.bytes[0] == 0xea); /* Segment based jump */
+}
+
+/* Check whether insn jumps into specified address range */
+static int insn_jump_into_range(struct insn *insn, unsigned long start, int len)
+{
+       unsigned long target = 0;
+
+       switch (insn->opcode.bytes[0]) {
+       case 0xe0:      /* loopne */
+       case 0xe1:      /* loope */
+       case 0xe2:      /* loop */
+       case 0xe3:      /* jcxz */
+       case 0xe9:      /* near relative jump */
+       case 0xeb:      /* short relative jump */
+               break;
+       case 0x0f:
+               if ((insn->opcode.bytes[1] & 0xf0) == 0x80) /* jcc near */
+                       break;
+               return 0;
+       default:
+               if ((insn->opcode.bytes[0] & 0xf0) == 0x70) /* jcc short */
+                       break;
+               return 0;
+       }
+       target = (unsigned long)insn->next_byte + insn->immediate.value;
+
+       return (start <= target && target <= start + len);
+}
+
+/* Decode whole function to ensure any instructions don't jump into target */
+static int __kprobes can_optimize(unsigned long paddr)
+{
+       int ret;
+       unsigned long addr, size = 0, offset = 0;
+       struct insn insn;
+       kprobe_opcode_t buf[MAX_INSN_SIZE];
+       /* Dummy buffers for lookup_symbol_attrs */
+       static char __dummy_buf[KSYM_NAME_LEN];
+
+       /* Lookup symbol including addr */
+       if (!kallsyms_lookup(paddr, &size, &offset, NULL, __dummy_buf))
+               return 0;
+
+       /* Check there is enough space for a relative jump. */
+       if (size - offset < RELATIVEJUMP_SIZE)
+               return 0;
+
+       /* Decode instructions */
+       addr = paddr - offset;
+       while (addr < paddr - offset + size) { /* Decode until function end */
+               if (search_exception_tables(addr))
+                       /*
+                        * Since some fixup code will jumps into this function,
+                        * we can't optimize kprobe in this function.
+                        */
+                       return 0;
+               kernel_insn_init(&insn, (void *)addr);
+               insn_get_opcode(&insn);
+               if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) {
+                       ret = recover_probed_instruction(buf, addr);
+                       if (ret)
+                               return 0;
+                       kernel_insn_init(&insn, buf);
+               }
+               insn_get_length(&insn);
+               /* Recover address */
+               insn.kaddr = (void *)addr;
+               insn.next_byte = (void *)(addr + insn.length);
+               /* Check any instructions don't jump into target */
+               if (insn_is_indirect_jump(&insn) ||
+                   insn_jump_into_range(&insn, paddr + INT3_SIZE,
+                                        RELATIVE_ADDR_SIZE))
+                       return 0;
+               addr += insn.length;
+       }
+
+       return 1;
+}
+
+/* Check optimized_kprobe can actually be optimized. */
+int __kprobes arch_check_optimized_kprobe(struct optimized_kprobe *op)
+{
+       int i;
+       struct kprobe *p;
+
+       for (i = 1; i < op->optinsn.size; i++) {
+               p = get_kprobe(op->kp.addr + i);
+               if (p && !kprobe_disabled(p))
+                       return -EEXIST;
+       }
+
+       return 0;
+}
+
+/* Check the addr is within the optimized instructions. */
+int __kprobes arch_within_optimized_kprobe(struct optimized_kprobe *op,
+                                          unsigned long addr)
+{
+       return ((unsigned long)op->kp.addr <= addr &&
+               (unsigned long)op->kp.addr + op->optinsn.size > addr);
+}
+
+/* Free optimized instruction slot */
+static __kprobes
+void __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
+{
+       if (op->optinsn.insn) {
+               free_optinsn_slot(op->optinsn.insn, dirty);
+               op->optinsn.insn = NULL;
+               op->optinsn.size = 0;
+       }
+}
+
+void __kprobes arch_remove_optimized_kprobe(struct optimized_kprobe *op)
+{
+       __arch_remove_optimized_kprobe(op, 1);
+}
+
+/*
+ * Copy replacing target instructions
+ * Target instructions MUST be relocatable (checked inside)
+ */
+int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
+{
+       u8 *buf;
+       int ret;
+       long rel;
+
+       if (!can_optimize((unsigned long)op->kp.addr))
+               return -EILSEQ;
+
+       op->optinsn.insn = get_optinsn_slot();
+       if (!op->optinsn.insn)
+               return -ENOMEM;
+
+       /*
+        * Verify if the address gap is in 2GB range, because this uses
+        * a relative jump.
+        */
+       rel = (long)op->optinsn.insn - (long)op->kp.addr + RELATIVEJUMP_SIZE;
+       if (abs(rel) > 0x7fffffff)
+               return -ERANGE;
+
+       buf = (u8 *)op->optinsn.insn;
+
+       /* Copy instructions into the out-of-line buffer */
+       ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr);
+       if (ret < 0) {
+               __arch_remove_optimized_kprobe(op, 0);
+               return ret;
+       }
+       op->optinsn.size = ret;
+
+       /* Copy arch-dep-instance from template */
+       memcpy(buf, &optprobe_template_entry, TMPL_END_IDX);
+
+       /* Set probe information */
+       synthesize_set_arg1(buf + TMPL_MOVE_IDX, (unsigned long)op);
+
+       /* Set probe function call */
+       synthesize_relcall(buf + TMPL_CALL_IDX, optimized_callback);
+
+       /* Set returning jmp instruction at the tail of out-of-line buffer */
+       synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
+                          (u8 *)op->kp.addr + op->optinsn.size);
+
+       flush_icache_range((unsigned long) buf,
+                          (unsigned long) buf + TMPL_END_IDX +
+                          op->optinsn.size + RELATIVEJUMP_SIZE);
+       return 0;
+}
+
+/* Replace a breakpoint (int3) with a relative jump.  */
+int __kprobes arch_optimize_kprobe(struct optimized_kprobe *op)
+{
+       unsigned char jmp_code[RELATIVEJUMP_SIZE];
+       s32 rel = (s32)((long)op->optinsn.insn -
+                       ((long)op->kp.addr + RELATIVEJUMP_SIZE));
+
+       /* Backup instructions which will be replaced by jump address */
+       memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE,
+              RELATIVE_ADDR_SIZE);
+
+       jmp_code[0] = RELATIVEJUMP_OPCODE;
+       *(s32 *)(&jmp_code[1]) = rel;
+
+       /*
+        * text_poke_smp doesn't support NMI/MCE code modifying.
+        * However, since kprobes itself also doesn't support NMI/MCE
+        * code probing, it's not a problem.
+        */
+       text_poke_smp(op->kp.addr, jmp_code, RELATIVEJUMP_SIZE);
+       return 0;
+}
+
+/* Replace a relative jump with a breakpoint (int3).  */
+void __kprobes arch_unoptimize_kprobe(struct optimized_kprobe *op)
+{
+       u8 buf[RELATIVEJUMP_SIZE];
+
+       /* Set int3 to first byte for kprobes */
+       buf[0] = BREAKPOINT_INSTRUCTION;
+       memcpy(buf + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);
+       text_poke_smp(op->kp.addr, buf, RELATIVEJUMP_SIZE);
+}
+
+static int  __kprobes setup_detour_execution(struct kprobe *p,
+                                            struct pt_regs *regs,
+                                            int reenter)
+{
+       struct optimized_kprobe *op;
+
+       if (p->flags & KPROBE_FLAG_OPTIMIZED) {
+               /* This kprobe is really able to run optimized path. */
+               op = container_of(p, struct optimized_kprobe, kp);
+               /* Detour through copied instructions */
+               regs->ip = (unsigned long)op->optinsn.insn + TMPL_END_IDX;
+               if (!reenter)
+                       reset_current_kprobe();
+               preempt_enable_no_resched();
+               return 1;
+       }
+       return 0;
+}
+#endif
+
 int __init arch_init_kprobes(void)
 {
        return 0;
index 9055e5872ff0a5afa668d1d7fa9517eeb6f618c8..1c0c6ab9c60f03394af20e57b3a09186a6cba57d 100644 (file)
@@ -301,7 +301,8 @@ static int __init vsyscall_init(void)
        register_sysctl_table(kernel_root_table2);
 #endif
        on_each_cpu(cpu_vsyscall_init, NULL, 1);
-       hotcpu_notifier(cpu_vsyscall_notifier, 0);
+       /* notifier priority > KVM */
+       hotcpu_notifier(cpu_vsyscall_notifier, 30);
        return 0;
 }
 
index 3c4d0109ad2051c71d9022c6d370a9ac47234cb0..970bbd4795161777e11b629151577160e5fc929e 100644 (file)
@@ -29,6 +29,7 @@ config KVM
        select HAVE_KVM_EVENTFD
        select KVM_APIC_ARCHITECTURE
        select USER_RETURN_NOTIFIER
+       select KVM_MMIO
        ---help---
          Support hosting fully virtualized guest machines using hardware
          virtualization extensions.  You will need a fairly recent
index 7e8faea4651e1a89dc2755f044681b11f94a78e2..4dade6ac08276537b07824ab7c23224d93946905 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/module.h>
 #include <asm/kvm_emulate.h>
 
-#include "mmu.h"               /* for is_long_mode() */
+#include "x86.h"
 
 /*
  * Opcode effective-address decode tables.
@@ -76,6 +76,8 @@
 #define GroupDual   (1<<15)     /* Alternate decoding of mod == 3 */
 #define GroupMask   0xff        /* Group number stored in bits 0:7 */
 /* Misc flags */
+#define Lock        (1<<26) /* lock prefix is allowed for the instruction */
+#define Priv        (1<<27) /* instruction generates #GP if current CPL != 0 */
 #define No64       (1<<28)
 /* Source 2 operand type */
 #define Src2None    (0<<29)
 enum {
        Group1_80, Group1_81, Group1_82, Group1_83,
        Group1A, Group3_Byte, Group3, Group4, Group5, Group7,
+       Group8, Group9,
 };
 
 static u32 opcode_table[256] = {
        /* 0x00 - 0x07 */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        ByteOp | DstAcc | SrcImm, DstAcc | SrcImm,
        ImplicitOps | Stack | No64, ImplicitOps | Stack | No64,
        /* 0x08 - 0x0F */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        ByteOp | DstAcc | SrcImm, DstAcc | SrcImm,
        ImplicitOps | Stack | No64, 0,
        /* 0x10 - 0x17 */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        ByteOp | DstAcc | SrcImm, DstAcc | SrcImm,
        ImplicitOps | Stack | No64, ImplicitOps | Stack | No64,
        /* 0x18 - 0x1F */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        ByteOp | DstAcc | SrcImm, DstAcc | SrcImm,
        ImplicitOps | Stack | No64, ImplicitOps | Stack | No64,
        /* 0x20 - 0x27 */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        DstAcc | SrcImmByte, DstAcc | SrcImm, 0, 0,
        /* 0x28 - 0x2F */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        0, 0, 0, 0,
        /* 0x30 - 0x37 */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
        0, 0, 0, 0,
        /* 0x38 - 0x3F */
@@ -156,7 +159,7 @@ static u32 opcode_table[256] = {
        Group | Group1_80, Group | Group1_81,
        Group | Group1_82, Group | Group1_83,
        ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
        /* 0x88 - 0x8F */
        ByteOp | DstMem | SrcReg | ModRM | Mov, DstMem | SrcReg | ModRM | Mov,
        ByteOp | DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
@@ -210,7 +213,7 @@ static u32 opcode_table[256] = {
        SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
        /* 0xF0 - 0xF7 */
        0, 0, 0, 0,
-       ImplicitOps, ImplicitOps, Group | Group3_Byte, Group | Group3,
+       ImplicitOps | Priv, ImplicitOps, Group | Group3_Byte, Group | Group3,
        /* 0xF8 - 0xFF */
        ImplicitOps, 0, ImplicitOps, ImplicitOps,
        ImplicitOps, ImplicitOps, Group | Group4, Group | Group5,
@@ -218,16 +221,20 @@ static u32 opcode_table[256] = {
 
 static u32 twobyte_table[256] = {
        /* 0x00 - 0x0F */
-       0, Group | GroupDual | Group7, 0, 0, 0, ImplicitOps, ImplicitOps, 0,
-       ImplicitOps, ImplicitOps, 0, 0, 0, ImplicitOps | ModRM, 0, 0,
+       0, Group | GroupDual | Group7, 0, 0,
+       0, ImplicitOps, ImplicitOps | Priv, 0,
+       ImplicitOps | Priv, ImplicitOps | Priv, 0, 0,
+       0, ImplicitOps | ModRM, 0, 0,
        /* 0x10 - 0x1F */
        0, 0, 0, 0, 0, 0, 0, 0, ImplicitOps | ModRM, 0, 0, 0, 0, 0, 0, 0,
        /* 0x20 - 0x2F */
-       ModRM | ImplicitOps, ModRM, ModRM | ImplicitOps, ModRM, 0, 0, 0, 0,
+       ModRM | ImplicitOps | Priv, ModRM | Priv,
+       ModRM | ImplicitOps | Priv, ModRM | Priv,
+       0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        /* 0x30 - 0x3F */
-       ImplicitOps, 0, ImplicitOps, 0,
-       ImplicitOps, ImplicitOps, 0, 0,
+       ImplicitOps | Priv, 0, ImplicitOps | Priv, 0,
+       ImplicitOps, ImplicitOps | Priv, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0,
        /* 0x40 - 0x47 */
        DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
@@ -257,21 +264,23 @@ static u32 twobyte_table[256] = {
        DstMem | SrcReg | Src2CL | ModRM, 0, 0,
        /* 0xA8 - 0xAF */
        ImplicitOps | Stack, ImplicitOps | Stack,
-       0, DstMem | SrcReg | ModRM | BitOp,
+       0, DstMem | SrcReg | ModRM | BitOp | Lock,
        DstMem | SrcReg | Src2ImmByte | ModRM,
        DstMem | SrcReg | Src2CL | ModRM,
        ModRM, 0,
        /* 0xB0 - 0xB7 */
-       ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM, 0,
-           DstMem | SrcReg | ModRM | BitOp,
+       ByteOp | DstMem | SrcReg | ModRM | Lock, DstMem | SrcReg | ModRM | Lock,
+       0, DstMem | SrcReg | ModRM | BitOp | Lock,
        0, 0, ByteOp | DstReg | SrcMem | ModRM | Mov,
            DstReg | SrcMem16 | ModRM | Mov,
        /* 0xB8 - 0xBF */
-       0, 0, DstMem | SrcImmByte | ModRM, DstMem | SrcReg | ModRM | BitOp,
+       0, 0,
+       Group | Group8, DstMem | SrcReg | ModRM | BitOp | Lock,
        0, 0, ByteOp | DstReg | SrcMem | ModRM | Mov,
            DstReg | SrcMem16 | ModRM | Mov,
        /* 0xC0 - 0xCF */
-       0, 0, 0, DstMem | SrcReg | ModRM | Mov, 0, 0, 0, ImplicitOps | ModRM,
+       0, 0, 0, DstMem | SrcReg | ModRM | Mov,
+       0, 0, 0, Group | GroupDual | Group9,
        0, 0, 0, 0, 0, 0, 0, 0,
        /* 0xD0 - 0xDF */
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -283,25 +292,41 @@ static u32 twobyte_table[256] = {
 
 static u32 group_table[] = {
        [Group1_80*8] =
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | Lock,
+       ByteOp | DstMem | SrcImm | ModRM,
        [Group1_81*8] =
-       DstMem | SrcImm | ModRM, DstMem | SrcImm | ModRM,
-       DstMem | SrcImm | ModRM, DstMem | SrcImm | ModRM,
-       DstMem | SrcImm | ModRM, DstMem | SrcImm | ModRM,
-       DstMem | SrcImm | ModRM, DstMem | SrcImm | ModRM,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM | Lock,
+       DstMem | SrcImm | ModRM,
        [Group1_82*8] =
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
-       ByteOp | DstMem | SrcImm | ModRM, ByteOp | DstMem | SrcImm | ModRM,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64 | Lock,
+       ByteOp | DstMem | SrcImm | ModRM | No64,
        [Group1_83*8] =
-       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM,
-       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM,
-       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM,
-       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM,
        [Group1A*8] =
        DstMem | SrcNone | ModRM | Mov | Stack, 0, 0, 0, 0, 0, 0, 0,
        [Group3_Byte*8] =
@@ -320,24 +345,39 @@ static u32 group_table[] = {
        SrcMem | ModRM | Stack, 0,
        SrcMem | ModRM | Stack, 0, SrcMem | ModRM | Stack, 0,
        [Group7*8] =
-       0, 0, ModRM | SrcMem, ModRM | SrcMem,
+       0, 0, ModRM | SrcMem | Priv, ModRM | SrcMem | Priv,
        SrcNone | ModRM | DstMem | Mov, 0,
-       SrcMem16 | ModRM | Mov, SrcMem | ModRM | ByteOp,
+       SrcMem16 | ModRM | Mov | Priv, SrcMem | ModRM | ByteOp | Priv,
+       [Group8*8] =
+       0, 0, 0, 0,
+       DstMem | SrcImmByte | ModRM, DstMem | SrcImmByte | ModRM | Lock,
+       DstMem | SrcImmByte | ModRM | Lock, DstMem | SrcImmByte | ModRM | Lock,
+       [Group9*8] =
+       0, ImplicitOps | ModRM | Lock, 0, 0, 0, 0, 0, 0,
 };
 
 static u32 group2_table[] = {
        [Group7*8] =
-       SrcNone | ModRM, 0, 0, SrcNone | ModRM,
+       SrcNone | ModRM | Priv, 0, 0, SrcNone | ModRM,
        SrcNone | ModRM | DstMem | Mov, 0,
        SrcMem16 | ModRM | Mov, 0,
+       [Group9*8] =
+       0, 0, 0, 0, 0, 0, 0, 0,
 };
 
 /* EFLAGS bit definitions. */
+#define EFLG_ID (1<<21)
+#define EFLG_VIP (1<<20)
+#define EFLG_VIF (1<<19)
+#define EFLG_AC (1<<18)
 #define EFLG_VM (1<<17)
 #define EFLG_RF (1<<16)
+#define EFLG_IOPL (3<<12)
+#define EFLG_NT (1<<14)
 #define EFLG_OF (1<<11)
 #define EFLG_DF (1<<10)
 #define EFLG_IF (1<<9)
+#define EFLG_TF (1<<8)
 #define EFLG_SF (1<<7)
 #define EFLG_ZF (1<<6)
 #define EFLG_AF (1<<4)
@@ -606,7 +646,7 @@ static int do_fetch_insn_byte(struct x86_emulate_ctxt *ctxt,
 
        if (linear < fc->start || linear >= fc->end) {
                size = min(15UL, PAGE_SIZE - offset_in_page(linear));
-               rc = ops->read_std(linear, fc->data, size, ctxt->vcpu);
+               rc = ops->fetch(linear, fc->data, size, ctxt->vcpu, NULL);
                if (rc)
                        return rc;
                fc->start = linear;
@@ -661,11 +701,11 @@ static int read_descriptor(struct x86_emulate_ctxt *ctxt,
                op_bytes = 3;
        *address = 0;
        rc = ops->read_std((unsigned long)ptr, (unsigned long *)size, 2,
-                          ctxt->vcpu);
+                          ctxt->vcpu, NULL);
        if (rc)
                return rc;
        rc = ops->read_std((unsigned long)ptr + 2, address, op_bytes,
-                          ctxt->vcpu);
+                          ctxt->vcpu, NULL);
        return rc;
 }
 
@@ -889,6 +929,7 @@ x86_decode_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
 
        switch (mode) {
        case X86EMUL_MODE_REAL:
+       case X86EMUL_MODE_VM86:
        case X86EMUL_MODE_PROT16:
                def_op_bytes = def_ad_bytes = 2;
                break;
@@ -975,7 +1016,7 @@ done_prefixes:
        }
 
        if (mode == X86EMUL_MODE_PROT64 && (c->d & No64)) {
-               kvm_report_emulation_failure(ctxt->vcpu, "invalid x86/64 instruction");;
+               kvm_report_emulation_failure(ctxt->vcpu, "invalid x86/64 instruction");
                return -1;
        }
 
@@ -1196,13 +1237,56 @@ static int emulate_pop(struct x86_emulate_ctxt *ctxt,
        rc = ops->read_emulated(register_address(c, ss_base(ctxt),
                                                 c->regs[VCPU_REGS_RSP]),
                                dest, len, ctxt->vcpu);
-       if (rc != 0)
+       if (rc != X86EMUL_CONTINUE)
                return rc;
 
        register_address_increment(c, &c->regs[VCPU_REGS_RSP], len);
        return rc;
 }
 
+static int emulate_popf(struct x86_emulate_ctxt *ctxt,
+                      struct x86_emulate_ops *ops,
+                      void *dest, int len)
+{
+       int rc;
+       unsigned long val, change_mask;
+       int iopl = (ctxt->eflags & X86_EFLAGS_IOPL) >> IOPL_SHIFT;
+       int cpl = kvm_x86_ops->get_cpl(ctxt->vcpu);
+
+       rc = emulate_pop(ctxt, ops, &val, len);
+       if (rc != X86EMUL_CONTINUE)
+               return rc;
+
+       change_mask = EFLG_CF | EFLG_PF | EFLG_AF | EFLG_ZF | EFLG_SF | EFLG_OF
+               | EFLG_TF | EFLG_DF | EFLG_NT | EFLG_RF | EFLG_AC | EFLG_ID;
+
+       switch(ctxt->mode) {
+       case X86EMUL_MODE_PROT64:
+       case X86EMUL_MODE_PROT32:
+       case X86EMUL_MODE_PROT16:
+               if (cpl == 0)
+                       change_mask |= EFLG_IOPL;
+               if (cpl <= iopl)
+                       change_mask |= EFLG_IF;
+               break;
+       case X86EMUL_MODE_VM86:
+               if (iopl < 3) {
+                       kvm_inject_gp(ctxt->vcpu, 0);
+                       return X86EMUL_PROPAGATE_FAULT;
+               }
+               change_mask |= EFLG_IF;
+               break;
+       default: /* real mode */
+               change_mask |= (EFLG_IOPL | EFLG_IF);
+               break;
+       }
+
+       *(unsigned long *)dest =
+               (ctxt->eflags & ~change_mask) | (val & change_mask);
+
+       return rc;
+}
+
 static void emulate_push_sreg(struct x86_emulate_ctxt *ctxt, int seg)
 {
        struct decode_cache *c = &ctxt->decode;
@@ -1225,7 +1309,7 @@ static int emulate_pop_sreg(struct x86_emulate_ctxt *ctxt,
        if (rc != 0)
                return rc;
 
-       rc = kvm_load_segment_descriptor(ctxt->vcpu, (u16)selector, 1, seg);
+       rc = kvm_load_segment_descriptor(ctxt->vcpu, (u16)selector, seg);
        return rc;
 }
 
@@ -1370,7 +1454,7 @@ static inline int emulate_grp9(struct x86_emulate_ctxt *ctxt,
        int rc;
 
        rc = ops->read_emulated(memop, &old, 8, ctxt->vcpu);
-       if (rc != 0)
+       if (rc != X86EMUL_CONTINUE)
                return rc;
 
        if (((u32) (old >> 0) != (u32) c->regs[VCPU_REGS_RAX]) ||
@@ -1385,7 +1469,7 @@ static inline int emulate_grp9(struct x86_emulate_ctxt *ctxt,
                       (u32) c->regs[VCPU_REGS_RBX];
 
                rc = ops->cmpxchg_emulated(memop, &old, &new, 8, ctxt->vcpu);
-               if (rc != 0)
+               if (rc != X86EMUL_CONTINUE)
                        return rc;
                ctxt->eflags |= EFLG_ZF;
        }
@@ -1407,7 +1491,7 @@ static int emulate_ret_far(struct x86_emulate_ctxt *ctxt,
        rc = emulate_pop(ctxt, ops, &cs, c->op_bytes);
        if (rc)
                return rc;
-       rc = kvm_load_segment_descriptor(ctxt->vcpu, (u16)cs, 1, VCPU_SREG_CS);
+       rc = kvm_load_segment_descriptor(ctxt->vcpu, (u16)cs, VCPU_SREG_CS);
        return rc;
 }
 
@@ -1451,7 +1535,7 @@ static inline int writeback(struct x86_emulate_ctxt *ctxt,
                                        &c->dst.val,
                                        c->dst.bytes,
                                        ctxt->vcpu);
-               if (rc != 0)
+               if (rc != X86EMUL_CONTINUE)
                        return rc;
                break;
        case OP_NONE:
@@ -1514,9 +1598,8 @@ emulate_syscall(struct x86_emulate_ctxt *ctxt)
        u64 msr_data;
 
        /* syscall is not available in real mode */
-       if (c->lock_prefix || ctxt->mode == X86EMUL_MODE_REAL
-               || !(ctxt->vcpu->arch.cr0 & X86_CR0_PE))
-               return -1;
+       if (ctxt->mode == X86EMUL_MODE_REAL || ctxt->mode == X86EMUL_MODE_VM86)
+               return X86EMUL_UNHANDLEABLE;
 
        setup_syscalls_segments(ctxt, &cs, &ss);
 
@@ -1553,7 +1636,7 @@ emulate_syscall(struct x86_emulate_ctxt *ctxt)
                ctxt->eflags &= ~(EFLG_VM | EFLG_IF | EFLG_RF);
        }
 
-       return 0;
+       return X86EMUL_CONTINUE;
 }
 
 static int
@@ -1563,22 +1646,17 @@ emulate_sysenter(struct x86_emulate_ctxt *ctxt)
        struct kvm_segment cs, ss;
        u64 msr_data;
 
-       /* inject #UD if LOCK prefix is used */
-       if (c->lock_prefix)
-               return -1;
-
-       /* inject #GP if in real mode or paging is disabled */
-       if (ctxt->mode == X86EMUL_MODE_REAL ||
-               !(ctxt->vcpu->arch.cr0 & X86_CR0_PE)) {
+       /* inject #GP if in real mode */
+       if (ctxt->mode == X86EMUL_MODE_REAL) {
                kvm_inject_gp(ctxt->vcpu, 0);
-               return -1;
+               return X86EMUL_UNHANDLEABLE;
        }
 
        /* XXX sysenter/sysexit have not been tested in 64bit mode.
        * Therefore, we inject an #UD.
        */
        if (ctxt->mode == X86EMUL_MODE_PROT64)
-               return -1;
+               return X86EMUL_UNHANDLEABLE;
 
        setup_syscalls_segments(ctxt, &cs, &ss);
 
@@ -1587,13 +1665,13 @@ emulate_sysenter(struct x86_emulate_ctxt *ctxt)
        case X86EMUL_MODE_PROT32:
                if ((msr_data & 0xfffc) == 0x0) {
                        kvm_inject_gp(ctxt->vcpu, 0);
-                       return -1;
+                       return X86EMUL_PROPAGATE_FAULT;
                }
                break;
        case X86EMUL_MODE_PROT64:
                if (msr_data == 0x0) {
                        kvm_inject_gp(ctxt->vcpu, 0);
-                       return -1;
+                       return X86EMUL_PROPAGATE_FAULT;
                }
                break;
        }
@@ -1618,7 +1696,7 @@ emulate_sysenter(struct x86_emulate_ctxt *ctxt)
        kvm_x86_ops->get_msr(ctxt->vcpu, MSR_IA32_SYSENTER_ESP, &msr_data);
        c->regs[VCPU_REGS_RSP] = msr_data;
 
-       return 0;
+       return X86EMUL_CONTINUE;
 }
 
 static int
@@ -1629,21 +1707,11 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt)
        u64 msr_data;
        int usermode;
 
-       /* inject #UD if LOCK prefix is used */
-       if (c->lock_prefix)
-               return -1;
-
-       /* inject #GP if in real mode or paging is disabled */
-       if (ctxt->mode == X86EMUL_MODE_REAL
-               || !(ctxt->vcpu->arch.cr0 & X86_CR0_PE)) {
-               kvm_inject_gp(ctxt->vcpu, 0);
-               return -1;
-       }
-
-       /* sysexit must be called from CPL 0 */
-       if (kvm_x86_ops->get_cpl(ctxt->vcpu) != 0) {
+       /* inject #GP if in real mode or Virtual 8086 mode */
+       if (ctxt->mode == X86EMUL_MODE_REAL ||
+           ctxt->mode == X86EMUL_MODE_VM86) {
                kvm_inject_gp(ctxt->vcpu, 0);
-               return -1;
+               return X86EMUL_UNHANDLEABLE;
        }
 
        setup_syscalls_segments(ctxt, &cs, &ss);
@@ -1661,7 +1729,7 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt)
                cs.selector = (u16)(msr_data + 16);
                if ((msr_data & 0xfffc) == 0x0) {
                        kvm_inject_gp(ctxt->vcpu, 0);
-                       return -1;
+                       return X86EMUL_PROPAGATE_FAULT;
                }
                ss.selector = (u16)(msr_data + 24);
                break;
@@ -1669,7 +1737,7 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt)
                cs.selector = (u16)(msr_data + 32);
                if (msr_data == 0x0) {
                        kvm_inject_gp(ctxt->vcpu, 0);
-                       return -1;
+                       return X86EMUL_PROPAGATE_FAULT;
                }
                ss.selector = cs.selector + 8;
                cs.db = 0;
@@ -1685,7 +1753,58 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt)
        c->eip = ctxt->vcpu->arch.regs[VCPU_REGS_RDX];
        c->regs[VCPU_REGS_RSP] = ctxt->vcpu->arch.regs[VCPU_REGS_RCX];
 
-       return 0;
+       return X86EMUL_CONTINUE;
+}
+
+static bool emulator_bad_iopl(struct x86_emulate_ctxt *ctxt)
+{
+       int iopl;
+       if (ctxt->mode == X86EMUL_MODE_REAL)
+               return false;
+       if (ctxt->mode == X86EMUL_MODE_VM86)
+               return true;
+       iopl = (ctxt->eflags & X86_EFLAGS_IOPL) >> IOPL_SHIFT;
+       return kvm_x86_ops->get_cpl(ctxt->vcpu) > iopl;
+}
+
+static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt,
+                                           struct x86_emulate_ops *ops,
+                                           u16 port, u16 len)
+{
+       struct kvm_segment tr_seg;
+       int r;
+       u16 io_bitmap_ptr;
+       u8 perm, bit_idx = port & 0x7;
+       unsigned mask = (1 << len) - 1;
+
+       kvm_get_segment(ctxt->vcpu, &tr_seg, VCPU_SREG_TR);
+       if (tr_seg.unusable)
+               return false;
+       if (tr_seg.limit < 103)
+               return false;
+       r = ops->read_std(tr_seg.base + 102, &io_bitmap_ptr, 2, ctxt->vcpu,
+                         NULL);
+       if (r != X86EMUL_CONTINUE)
+               return false;
+       if (io_bitmap_ptr + port/8 > tr_seg.limit)
+               return false;
+       r = ops->read_std(tr_seg.base + io_bitmap_ptr + port/8, &perm, 1,
+                         ctxt->vcpu, NULL);
+       if (r != X86EMUL_CONTINUE)
+               return false;
+       if ((perm >> bit_idx) & mask)
+               return false;
+       return true;
+}
+
+static bool emulator_io_permited(struct x86_emulate_ctxt *ctxt,
+                                struct x86_emulate_ops *ops,
+                                u16 port, u16 len)
+{
+       if (emulator_bad_iopl(ctxt))
+               if (!emulator_io_port_access_allowed(ctxt, ops, port, len))
+                       return false;
+       return true;
 }
 
 int
@@ -1709,6 +1828,18 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
        memcpy(c->regs, ctxt->vcpu->arch.regs, sizeof c->regs);
        saved_eip = c->eip;
 
+       /* LOCK prefix is allowed only with some instructions */
+       if (c->lock_prefix && !(c->d & Lock)) {
+               kvm_queue_exception(ctxt->vcpu, UD_VECTOR);
+               goto done;
+       }
+
+       /* Privileged instruction can be executed only in CPL=0 */
+       if ((c->d & Priv) && kvm_x86_ops->get_cpl(ctxt->vcpu)) {
+               kvm_inject_gp(ctxt->vcpu, 0);
+               goto done;
+       }
+
        if (((c->d & ModRM) && (c->modrm_mod != 3)) || (c->d & MemAbs))
                memop = c->modrm_ea;
 
@@ -1749,7 +1880,7 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
                                        &c->src.val,
                                        c->src.bytes,
                                        ctxt->vcpu);
-               if (rc != 0)
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
                c->src.orig_val = c->src.val;
        }
@@ -1768,12 +1899,15 @@ x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
                        c->dst.ptr = (void *)c->dst.ptr +
                                                   (c->src.val & mask) / 8;
                }
-               if (!(c->d & Mov) &&
-                                  /* optimisation - avoid slow emulated read */
-                   ((rc = ops->read_emulated((unsigned long)c->dst.ptr,
-                                          &c->dst.val,
-                                         c->dst.bytes, ctxt->vcpu)) != 0))
-                       goto done;
+               if (!(c->d & Mov)) {
+                       /* optimisation - avoid slow emulated read */
+                       rc = ops->read_emulated((unsigned long)c->dst.ptr,
+                                               &c->dst.val,
+                                               c->dst.bytes,
+                                               ctxt->vcpu);
+                       if (rc != X86EMUL_CONTINUE)
+                               goto done;
+               }
        }
        c->dst.orig_val = c->dst.val;
 
@@ -1876,7 +2010,12 @@ special_insn:
                break;
        case 0x6c:              /* insb */
        case 0x6d:              /* insw/insd */
-                if (kvm_emulate_pio_string(ctxt->vcpu,
+               if (!emulator_io_permited(ctxt, ops, c->regs[VCPU_REGS_RDX],
+                                         (c->d & ByteOp) ? 1 : c->op_bytes)) {
+                       kvm_inject_gp(ctxt->vcpu, 0);
+                       goto done;
+               }
+               if (kvm_emulate_pio_string(ctxt->vcpu,
                                1,
                                (c->d & ByteOp) ? 1 : c->op_bytes,
                                c->rep_prefix ?
@@ -1892,6 +2031,11 @@ special_insn:
                return 0;
        case 0x6e:              /* outsb */
        case 0x6f:              /* outsw/outsd */
+               if (!emulator_io_permited(ctxt, ops, c->regs[VCPU_REGS_RDX],
+                                         (c->d & ByteOp) ? 1 : c->op_bytes)) {
+                       kvm_inject_gp(ctxt->vcpu, 0);
+                       goto done;
+               }
                if (kvm_emulate_pio_string(ctxt->vcpu,
                                0,
                                (c->d & ByteOp) ? 1 : c->op_bytes,
@@ -1978,25 +2122,19 @@ special_insn:
                break;
        case 0x8e: { /* mov seg, r/m16 */
                uint16_t sel;
-               int type_bits;
-               int err;
 
                sel = c->src.val;
-               if (c->modrm_reg == VCPU_SREG_SS)
-                       toggle_interruptibility(ctxt, X86_SHADOW_INT_MOV_SS);
 
-               if (c->modrm_reg <= 5) {
-                       type_bits = (c->modrm_reg == 1) ? 9 : 1;
-                       err = kvm_load_segment_descriptor(ctxt->vcpu, sel,
-                                                         type_bits, c->modrm_reg);
-               } else {
-                       printk(KERN_INFO "Invalid segreg in modrm byte 0x%02x\n",
-                                       c->modrm);
-                       goto cannot_emulate;
+               if (c->modrm_reg == VCPU_SREG_CS ||
+                   c->modrm_reg > VCPU_SREG_GS) {
+                       kvm_queue_exception(ctxt->vcpu, UD_VECTOR);
+                       goto done;
                }
 
-               if (err < 0)
-                       goto cannot_emulate;
+               if (c->modrm_reg == VCPU_SREG_SS)
+                       toggle_interruptibility(ctxt, X86_SHADOW_INT_MOV_SS);
+
+               rc = kvm_load_segment_descriptor(ctxt->vcpu, sel, c->modrm_reg);
 
                c->dst.type = OP_NONE;  /* Disable writeback. */
                break;
@@ -2025,7 +2163,10 @@ special_insn:
                c->dst.type = OP_REG;
                c->dst.ptr = (unsigned long *) &ctxt->eflags;
                c->dst.bytes = c->op_bytes;
-               goto pop_instruction;
+               rc = emulate_popf(ctxt, ops, &c->dst.val, c->op_bytes);
+               if (rc != X86EMUL_CONTINUE)
+                       goto done;
+               break;
        case 0xa0 ... 0xa1:     /* mov */
                c->dst.ptr = (unsigned long *)&c->regs[VCPU_REGS_RAX];
                c->dst.val = c->src.val;
@@ -2039,11 +2180,12 @@ special_insn:
                c->dst.ptr = (unsigned long *)register_address(c,
                                                   es_base(ctxt),
                                                   c->regs[VCPU_REGS_RDI]);
-               if ((rc = ops->read_emulated(register_address(c,
-                                          seg_override_base(ctxt, c),
-                                       c->regs[VCPU_REGS_RSI]),
+               rc = ops->read_emulated(register_address(c,
+                                               seg_override_base(ctxt, c),
+                                               c->regs[VCPU_REGS_RSI]),
                                        &c->dst.val,
-                                       c->dst.bytes, ctxt->vcpu)) != 0)
+                                       c->dst.bytes, ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
                register_address_increment(c, &c->regs[VCPU_REGS_RSI],
                                       (ctxt->eflags & EFLG_DF) ? -c->dst.bytes
@@ -2058,10 +2200,11 @@ special_insn:
                c->src.ptr = (unsigned long *)register_address(c,
                                       seg_override_base(ctxt, c),
                                                   c->regs[VCPU_REGS_RSI]);
-               if ((rc = ops->read_emulated((unsigned long)c->src.ptr,
-                                               &c->src.val,
-                                               c->src.bytes,
-                                               ctxt->vcpu)) != 0)
+               rc = ops->read_emulated((unsigned long)c->src.ptr,
+                                       &c->src.val,
+                                       c->src.bytes,
+                                       ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
 
                c->dst.type = OP_NONE; /* Disable writeback. */
@@ -2069,10 +2212,11 @@ special_insn:
                c->dst.ptr = (unsigned long *)register_address(c,
                                                   es_base(ctxt),
                                                   c->regs[VCPU_REGS_RDI]);
-               if ((rc = ops->read_emulated((unsigned long)c->dst.ptr,
-                                               &c->dst.val,
-                                               c->dst.bytes,
-                                               ctxt->vcpu)) != 0)
+               rc = ops->read_emulated((unsigned long)c->dst.ptr,
+                                       &c->dst.val,
+                                       c->dst.bytes,
+                                       ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
 
                DPRINTF("cmps: mem1=0x%p mem2=0x%p\n", c->src.ptr, c->dst.ptr);
@@ -2102,12 +2246,13 @@ special_insn:
                c->dst.type = OP_REG;
                c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
                c->dst.ptr = (unsigned long *)&c->regs[VCPU_REGS_RAX];
-               if ((rc = ops->read_emulated(register_address(c,
-                                                seg_override_base(ctxt, c),
-                                                c->regs[VCPU_REGS_RSI]),
-                                                &c->dst.val,
-                                                c->dst.bytes,
-                                                ctxt->vcpu)) != 0)
+               rc = ops->read_emulated(register_address(c,
+                                               seg_override_base(ctxt, c),
+                                               c->regs[VCPU_REGS_RSI]),
+                                       &c->dst.val,
+                                       c->dst.bytes,
+                                       ctxt->vcpu);
+               if (rc != X86EMUL_CONTINUE)
                        goto done;
                register_address_increment(c, &c->regs[VCPU_REGS_RSI],
                                       (ctxt->eflags & EFLG_DF) ? -c->dst.bytes
@@ -2163,11 +2308,9 @@ special_insn:
        case 0xe9: /* jmp rel */
                goto jmp;
        case 0xea: /* jmp far */
-               if (kvm_load_segment_descriptor(ctxt->vcpu, c->src2.val, 9,
-                                       VCPU_SREG_CS) < 0) {
-                       DPRINTF("jmp far: Failed to load CS descriptor\n");
-                       goto cannot_emulate;
-               }
+               if (kvm_load_segment_descriptor(ctxt->vcpu, c->src2.val,
+                                               VCPU_SREG_CS))
+                       goto done;
 
                c->eip = c->src.val;
                break;
@@ -2185,7 +2328,13 @@ special_insn:
        case 0xef: /* out (e/r)ax,dx */
                port = c->regs[VCPU_REGS_RDX];
                io_dir_in = 0;
-       do_io:  if (kvm_emulate_pio(ctxt->vcpu, io_dir_in,
+       do_io:
+               if (!emulator_io_permited(ctxt, ops, port,
+                                         (c->d & ByteOp) ? 1 : c->op_bytes)) {
+                       kvm_inject_gp(ctxt->vcpu, 0);
+                       goto done;
+               }
+               if (kvm_emulate_pio(ctxt->vcpu, io_dir_in,
                                   (c->d & ByteOp) ? 1 : c->op_bytes,
                                   port) != 0) {
                        c->eip = saved_eip;
@@ -2210,13 +2359,21 @@ special_insn:
                c->dst.type = OP_NONE;  /* Disable writeback. */
                break;
        case 0xfa: /* cli */
-               ctxt->eflags &= ~X86_EFLAGS_IF;
-               c->dst.type = OP_NONE;  /* Disable writeback. */
+               if (emulator_bad_iopl(ctxt))
+                       kvm_inject_gp(ctxt->vcpu, 0);
+               else {
+                       ctxt->eflags &= ~X86_EFLAGS_IF;
+                       c->dst.type = OP_NONE;  /* Disable writeback. */
+               }
                break;
        case 0xfb: /* sti */
-               toggle_interruptibility(ctxt, X86_SHADOW_INT_STI);
-               ctxt->eflags |= X86_EFLAGS_IF;
-               c->dst.type = OP_NONE;  /* Disable writeback. */
+               if (emulator_bad_iopl(ctxt))
+                       kvm_inject_gp(ctxt->vcpu, 0);
+               else {
+                       toggle_interruptibility(ctxt, X86_SHADOW_INT_STI);
+                       ctxt->eflags |= X86_EFLAGS_IF;
+                       c->dst.type = OP_NONE;  /* Disable writeback. */
+               }
                break;
        case 0xfc: /* cld */
                ctxt->eflags &= ~EFLG_DF;
@@ -2319,8 +2476,9 @@ twobyte_insn:
                }
                break;
        case 0x05:              /* syscall */
-               if (emulate_syscall(ctxt) == -1)
-                       goto cannot_emulate;
+               rc = emulate_syscall(ctxt);
+               if (rc != X86EMUL_CONTINUE)
+                       goto done;
                else
                        goto writeback;
                break;
@@ -2391,14 +2549,16 @@ twobyte_insn:
                c->dst.type = OP_NONE;
                break;
        case 0x34:              /* sysenter */
-               if (emulate_sysenter(ctxt) == -1)
-                       goto cannot_emulate;
+               rc = emulate_sysenter(ctxt);
+               if (rc != X86EMUL_CONTINUE)
+                       goto done;
                else
                        goto writeback;
                break;
        case 0x35:              /* sysexit */
-               if (emulate_sysexit(ctxt) == -1)
-                       goto cannot_emulate;
+               rc = emulate_sysexit(ctxt);
+               if (rc != X86EMUL_CONTINUE)
+                       goto done;
                else
                        goto writeback;
                break;
index 15578f180e596bee481f10451ec375b6ceb37f26..294698b6daff60c288dcd1304470ab7c6254000f 100644 (file)
@@ -242,11 +242,11 @@ static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian)
 {
        struct kvm_kpit_state *ps = container_of(kian, struct kvm_kpit_state,
                                                 irq_ack_notifier);
-       spin_lock(&ps->inject_lock);
+       raw_spin_lock(&ps->inject_lock);
        if (atomic_dec_return(&ps->pit_timer.pending) < 0)
                atomic_inc(&ps->pit_timer.pending);
        ps->irq_ack = 1;
-       spin_unlock(&ps->inject_lock);
+       raw_spin_unlock(&ps->inject_lock);
 }
 
 void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
@@ -605,7 +605,7 @@ static const struct kvm_io_device_ops speaker_dev_ops = {
        .write    = speaker_ioport_write,
 };
 
-/* Caller must have writers lock on slots_lock */
+/* Caller must hold slots_lock */
 struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags)
 {
        struct kvm_pit *pit;
@@ -624,7 +624,7 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags)
 
        mutex_init(&pit->pit_state.lock);
        mutex_lock(&pit->pit_state.lock);
-       spin_lock_init(&pit->pit_state.inject_lock);
+       raw_spin_lock_init(&pit->pit_state.inject_lock);
 
        kvm->arch.vpit = pit;
        pit->kvm = kvm;
@@ -645,13 +645,13 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags)
        kvm_register_irq_mask_notifier(kvm, 0, &pit->mask_notifier);
 
        kvm_iodevice_init(&pit->dev, &pit_dev_ops);
-       ret = __kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
+       ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, &pit->dev);
        if (ret < 0)
                goto fail;
 
        if (flags & KVM_PIT_SPEAKER_DUMMY) {
                kvm_iodevice_init(&pit->speaker_dev, &speaker_dev_ops);
-               ret = __kvm_io_bus_register_dev(&kvm->pio_bus,
+               ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS,
                                                &pit->speaker_dev);
                if (ret < 0)
                        goto fail_unregister;
@@ -660,11 +660,12 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags)
        return pit;
 
 fail_unregister:
-       __kvm_io_bus_unregister_dev(&kvm->pio_bus, &pit->dev);
+       kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &pit->dev);
 
 fail:
-       if (pit->irq_source_id >= 0)
-               kvm_free_irq_source_id(kvm, pit->irq_source_id);
+       kvm_unregister_irq_mask_notifier(kvm, 0, &pit->mask_notifier);
+       kvm_unregister_irq_ack_notifier(kvm, &pit_state->irq_ack_notifier);
+       kvm_free_irq_source_id(kvm, pit->irq_source_id);
 
        kfree(pit);
        return NULL;
@@ -723,12 +724,12 @@ void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
                /* Try to inject pending interrupts when
                 * last one has been acked.
                 */
-               spin_lock(&ps->inject_lock);
+               raw_spin_lock(&ps->inject_lock);
                if (atomic_read(&ps->pit_timer.pending) && ps->irq_ack) {
                        ps->irq_ack = 0;
                        inject = 1;
                }
-               spin_unlock(&ps->inject_lock);
+               raw_spin_unlock(&ps->inject_lock);
                if (inject)
                        __inject_pit_timer_intr(kvm);
        }
index d4c1c7ffdc099ac1e3153bf22724b5b2095d307e..900d6b0ba7c2347ed4fb487a92dc3a2d30c83a9f 100644 (file)
@@ -27,7 +27,7 @@ struct kvm_kpit_state {
        u32    speaker_data_on;
        struct mutex lock;
        struct kvm_pit *pit;
-       spinlock_t inject_lock;
+       raw_spinlock_t inject_lock;
        unsigned long irq_ack;
        struct kvm_irq_ack_notifier irq_ack_notifier;
 };
index d057c0cbd2457d1b665cdfa9fefd71cdbc726d22..07771da85de55a75db97f4c7522ad465ce386b26 100644 (file)
@@ -44,18 +44,19 @@ static void pic_clear_isr(struct kvm_kpic_state *s, int irq)
         * Other interrupt may be delivered to PIC while lock is dropped but
         * it should be safe since PIC state is already updated at this stage.
         */
-       spin_unlock(&s->pics_state->lock);
+       raw_spin_unlock(&s->pics_state->lock);
        kvm_notify_acked_irq(s->pics_state->kvm, SELECT_PIC(irq), irq);
-       spin_lock(&s->pics_state->lock);
+       raw_spin_lock(&s->pics_state->lock);
 }
 
 void kvm_pic_clear_isr_ack(struct kvm *kvm)
 {
        struct kvm_pic *s = pic_irqchip(kvm);
-       spin_lock(&s->lock);
+
+       raw_spin_lock(&s->lock);
        s->pics[0].isr_ack = 0xff;
        s->pics[1].isr_ack = 0xff;
-       spin_unlock(&s->lock);
+       raw_spin_unlock(&s->lock);
 }
 
 /*
@@ -156,9 +157,9 @@ static void pic_update_irq(struct kvm_pic *s)
 
 void kvm_pic_update_irq(struct kvm_pic *s)
 {
-       spin_lock(&s->lock);
+       raw_spin_lock(&s->lock);
        pic_update_irq(s);
-       spin_unlock(&s->lock);
+       raw_spin_unlock(&s->lock);
 }
 
 int kvm_pic_set_irq(void *opaque, int irq, int level)
@@ -166,14 +167,14 @@ int kvm_pic_set_irq(void *opaque, int irq, int level)
        struct kvm_pic *s = opaque;
        int ret = -1;
 
-       spin_lock(&s->lock);
+       raw_spin_lock(&s->lock);
        if (irq >= 0 && irq < PIC_NUM_PINS) {
                ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
                pic_update_irq(s);
                trace_kvm_pic_set_irq(irq >> 3, irq & 7, s->pics[irq >> 3].elcr,
                                      s->pics[irq >> 3].imr, ret == 0);
        }
-       spin_unlock(&s->lock);
+       raw_spin_unlock(&s->lock);
 
        return ret;
 }
@@ -203,7 +204,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
        int irq, irq2, intno;
        struct kvm_pic *s = pic_irqchip(kvm);
 
-       spin_lock(&s->lock);
+       raw_spin_lock(&s->lock);
        irq = pic_get_irq(&s->pics[0]);
        if (irq >= 0) {
                pic_intack(&s->pics[0], irq);
@@ -228,7 +229,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
                intno = s->pics[0].irq_base + irq;
        }
        pic_update_irq(s);
-       spin_unlock(&s->lock);
+       raw_spin_unlock(&s->lock);
 
        return intno;
 }
@@ -442,7 +443,7 @@ static int picdev_write(struct kvm_io_device *this,
                        printk(KERN_ERR "PIC: non byte write\n");
                return 0;
        }
-       spin_lock(&s->lock);
+       raw_spin_lock(&s->lock);
        switch (addr) {
        case 0x20:
        case 0x21:
@@ -455,7 +456,7 @@ static int picdev_write(struct kvm_io_device *this,
                elcr_ioport_write(&s->pics[addr & 1], addr, data);
                break;
        }
-       spin_unlock(&s->lock);
+       raw_spin_unlock(&s->lock);
        return 0;
 }
 
@@ -472,7 +473,7 @@ static int picdev_read(struct kvm_io_device *this,
                        printk(KERN_ERR "PIC: non byte read\n");
                return 0;
        }
-       spin_lock(&s->lock);
+       raw_spin_lock(&s->lock);
        switch (addr) {
        case 0x20:
        case 0x21:
@@ -486,7 +487,7 @@ static int picdev_read(struct kvm_io_device *this,
                break;
        }
        *(unsigned char *)val = data;
-       spin_unlock(&s->lock);
+       raw_spin_unlock(&s->lock);
        return 0;
 }
 
@@ -520,7 +521,7 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm)
        s = kzalloc(sizeof(struct kvm_pic), GFP_KERNEL);
        if (!s)
                return NULL;
-       spin_lock_init(&s->lock);
+       raw_spin_lock_init(&s->lock);
        s->kvm = kvm;
        s->pics[0].elcr_mask = 0xf8;
        s->pics[1].elcr_mask = 0xde;
@@ -533,7 +534,9 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm)
         * Initialize PIO device
         */
        kvm_iodevice_init(&s->dev, &picdev_ops);
-       ret = kvm_io_bus_register_dev(kvm, &kvm->pio_bus, &s->dev);
+       mutex_lock(&kvm->slots_lock);
+       ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, &s->dev);
+       mutex_unlock(&kvm->slots_lock);
        if (ret < 0) {
                kfree(s);
                return NULL;
@@ -541,3 +544,14 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm)
 
        return s;
 }
+
+void kvm_destroy_pic(struct kvm *kvm)
+{
+       struct kvm_pic *vpic = kvm->arch.vpic;
+
+       if (vpic) {
+               kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev);
+               kvm->arch.vpic = NULL;
+               kfree(vpic);
+       }
+}
index be399e207d57943ac74a6967e3dd21776b262dee..34b15915754d80f459088a1696c9d16f79c3bd5b 100644 (file)
@@ -62,7 +62,7 @@ struct kvm_kpic_state {
 };
 
 struct kvm_pic {
-       spinlock_t lock;
+       raw_spinlock_t lock;
        unsigned pending_acks;
        struct kvm *kvm;
        struct kvm_kpic_state pics[2]; /* 0 is master pic, 1 is slave pic */
@@ -75,6 +75,7 @@ struct kvm_pic {
 };
 
 struct kvm_pic *kvm_create_pic(struct kvm *kvm);
+void kvm_destroy_pic(struct kvm *kvm);
 int kvm_pic_read_irq(struct kvm *kvm);
 void kvm_pic_update_irq(struct kvm_pic *s);
 void kvm_pic_clear_isr_ack(struct kvm *kvm);
index 7bcc5b6a4403f6b3bcc816f8279c48c6ebb52ce8..cff851cf5322f59db69fc2b69d3ea306480e96d6 100644 (file)
@@ -1,6 +1,11 @@
 #ifndef ASM_KVM_CACHE_REGS_H
 #define ASM_KVM_CACHE_REGS_H
 
+#define KVM_POSSIBLE_CR0_GUEST_BITS X86_CR0_TS
+#define KVM_POSSIBLE_CR4_GUEST_BITS                              \
+       (X86_CR4_PVI | X86_CR4_DE | X86_CR4_PCE | X86_CR4_OSFXSR  \
+        | X86_CR4_OSXMMEXCPT | X86_CR4_PGE)
+
 static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu,
                                              enum kvm_reg reg)
 {
@@ -38,4 +43,30 @@ static inline u64 kvm_pdptr_read(struct kvm_vcpu *vcpu, int index)
        return vcpu->arch.pdptrs[index];
 }
 
+static inline ulong kvm_read_cr0_bits(struct kvm_vcpu *vcpu, ulong mask)
+{
+       ulong tmask = mask & KVM_POSSIBLE_CR0_GUEST_BITS;
+       if (tmask & vcpu->arch.cr0_guest_owned_bits)
+               kvm_x86_ops->decache_cr0_guest_bits(vcpu);
+       return vcpu->arch.cr0 & mask;
+}
+
+static inline ulong kvm_read_cr0(struct kvm_vcpu *vcpu)
+{
+       return kvm_read_cr0_bits(vcpu, ~0UL);
+}
+
+static inline ulong kvm_read_cr4_bits(struct kvm_vcpu *vcpu, ulong mask)
+{
+       ulong tmask = mask & KVM_POSSIBLE_CR4_GUEST_BITS;
+       if (tmask & vcpu->arch.cr4_guest_owned_bits)
+               kvm_x86_ops->decache_cr4_guest_bits(vcpu);
+       return vcpu->arch.cr4 & mask;
+}
+
+static inline ulong kvm_read_cr4(struct kvm_vcpu *vcpu)
+{
+       return kvm_read_cr4_bits(vcpu, ~0UL);
+}
+
 #endif
index ba8c045da7820fceea3286b2a60c1378fe28265d..4b224f90087bd602ce3c74a09392cb7dc5d23f07 100644 (file)
@@ -1246,3 +1246,34 @@ int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
 
        return 0;
 }
+
+int kvm_hv_vapic_msr_write(struct kvm_vcpu *vcpu, u32 reg, u64 data)
+{
+       struct kvm_lapic *apic = vcpu->arch.apic;
+
+       if (!irqchip_in_kernel(vcpu->kvm))
+               return 1;
+
+       /* if this is ICR write vector before command */
+       if (reg == APIC_ICR)
+               apic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
+       return apic_reg_write(apic, reg, (u32)data);
+}
+
+int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 reg, u64 *data)
+{
+       struct kvm_lapic *apic = vcpu->arch.apic;
+       u32 low, high = 0;
+
+       if (!irqchip_in_kernel(vcpu->kvm))
+               return 1;
+
+       if (apic_reg_read(apic, reg, 4, &low))
+               return 1;
+       if (reg == APIC_ICR)
+               apic_reg_read(apic, APIC_ICR2, 4, &high);
+
+       *data = (((u64)high) << 32) | low;
+
+       return 0;
+}
index 40010b09c4aa11a2c977e80ac314f5e43f1c8e44..f5fe32c5edadb852841fcfcc60b96758624ed92b 100644 (file)
@@ -48,4 +48,12 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);
 
 int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data);
 int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
+
+int kvm_hv_vapic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data);
+int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
+
+static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.hv_vapic & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE;
+}
 #endif
index 89a49fb46a275e6eebcf2609f3870696b3a3945b..741373e8ca777a318ed7e487ef8982a181ea24f4 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include "mmu.h"
+#include "x86.h"
 #include "kvm_cache_regs.h"
 
 #include <linux/kvm_host.h>
@@ -29,6 +30,7 @@
 #include <linux/swap.h>
 #include <linux/hugetlb.h>
 #include <linux/compiler.h>
+#include <linux/srcu.h>
 
 #include <asm/page.h>
 #include <asm/cmpxchg.h>
@@ -136,16 +138,6 @@ module_param(oos_shadow, bool, 0644);
 #define PT64_PERM_MASK (PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK \
                        | PT64_NX_MASK)
 
-#define PFERR_PRESENT_MASK (1U << 0)
-#define PFERR_WRITE_MASK (1U << 1)
-#define PFERR_USER_MASK (1U << 2)
-#define PFERR_RSVD_MASK (1U << 3)
-#define PFERR_FETCH_MASK (1U << 4)
-
-#define PT_PDPE_LEVEL 3
-#define PT_DIRECTORY_LEVEL 2
-#define PT_PAGE_TABLE_LEVEL 1
-
 #define RMAP_EXT 4
 
 #define ACC_EXEC_MASK    1
@@ -153,6 +145,9 @@ module_param(oos_shadow, bool, 0644);
 #define ACC_USER_MASK    PT_USER_MASK
 #define ACC_ALL          (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK)
 
+#include <trace/events/kvm.h>
+
+#undef TRACE_INCLUDE_FILE
 #define CREATE_TRACE_POINTS
 #include "mmutrace.h"
 
@@ -229,7 +224,7 @@ EXPORT_SYMBOL_GPL(kvm_mmu_set_mask_ptes);
 
 static int is_write_protection(struct kvm_vcpu *vcpu)
 {
-       return vcpu->arch.cr0 & X86_CR0_WP;
+       return kvm_read_cr0_bits(vcpu, X86_CR0_WP);
 }
 
 static int is_cpuid_PSE36(void)
@@ -239,7 +234,7 @@ static int is_cpuid_PSE36(void)
 
 static int is_nx(struct kvm_vcpu *vcpu)
 {
-       return vcpu->arch.shadow_efer & EFER_NX;
+       return vcpu->arch.efer & EFER_NX;
 }
 
 static int is_shadow_present_pte(u64 pte)
@@ -253,7 +248,7 @@ static int is_large_pte(u64 pte)
        return pte & PT_PAGE_SIZE_MASK;
 }
 
-static int is_writeble_pte(unsigned long pte)
+static int is_writable_pte(unsigned long pte)
 {
        return pte & PT_WRITABLE_MASK;
 }
@@ -470,24 +465,10 @@ static int has_wrprotected_page(struct kvm *kvm,
 
 static int host_mapping_level(struct kvm *kvm, gfn_t gfn)
 {
-       unsigned long page_size = PAGE_SIZE;
-       struct vm_area_struct *vma;
-       unsigned long addr;
+       unsigned long page_size;
        int i, ret = 0;
 
-       addr = gfn_to_hva(kvm, gfn);
-       if (kvm_is_error_hva(addr))
-               return PT_PAGE_TABLE_LEVEL;
-
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(current->mm, addr);
-       if (!vma)
-               goto out;
-
-       page_size = vma_kernel_pagesize(vma);
-
-out:
-       up_read(&current->mm->mmap_sem);
+       page_size = kvm_host_page_size(kvm, gfn);
 
        for (i = PT_PAGE_TABLE_LEVEL;
             i < (PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES); ++i) {
@@ -503,8 +484,7 @@ out:
 static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn)
 {
        struct kvm_memory_slot *slot;
-       int host_level;
-       int level = PT_PAGE_TABLE_LEVEL;
+       int host_level, level, max_level;
 
        slot = gfn_to_memslot(vcpu->kvm, large_gfn);
        if (slot && slot->dirty_bitmap)
@@ -515,7 +495,10 @@ static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn)
        if (host_level == PT_PAGE_TABLE_LEVEL)
                return host_level;
 
-       for (level = PT_DIRECTORY_LEVEL; level <= host_level; ++level)
+       max_level = kvm_x86_ops->get_lpage_level() < host_level ?
+               kvm_x86_ops->get_lpage_level() : host_level;
+
+       for (level = PT_DIRECTORY_LEVEL; level <= max_level; ++level)
                if (has_wrprotected_page(vcpu->kvm, large_gfn, level))
                        break;
 
@@ -633,7 +616,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte)
        pfn = spte_to_pfn(*spte);
        if (*spte & shadow_accessed_mask)
                kvm_set_pfn_accessed(pfn);
-       if (is_writeble_pte(*spte))
+       if (is_writable_pte(*spte))
                kvm_set_pfn_dirty(pfn);
        rmapp = gfn_to_rmap(kvm, sp->gfns[spte - sp->spt], sp->role.level);
        if (!*rmapp) {
@@ -662,6 +645,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte)
                        prev_desc = desc;
                        desc = desc->more;
                }
+               pr_err("rmap_remove: %p %llx many->many\n", spte, *spte);
                BUG();
        }
 }
@@ -708,7 +692,7 @@ static int rmap_write_protect(struct kvm *kvm, u64 gfn)
                BUG_ON(!spte);
                BUG_ON(!(*spte & PT_PRESENT_MASK));
                rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte);
-               if (is_writeble_pte(*spte)) {
+               if (is_writable_pte(*spte)) {
                        __set_spte(spte, *spte & ~PT_WRITABLE_MASK);
                        write_protected = 1;
                }
@@ -732,7 +716,7 @@ static int rmap_write_protect(struct kvm *kvm, u64 gfn)
                        BUG_ON(!(*spte & PT_PRESENT_MASK));
                        BUG_ON((*spte & (PT_PAGE_SIZE_MASK|PT_PRESENT_MASK)) != (PT_PAGE_SIZE_MASK|PT_PRESENT_MASK));
                        pgprintk("rmap_write_protect(large): spte %p %llx %lld\n", spte, *spte, gfn);
-                       if (is_writeble_pte(*spte)) {
+                       if (is_writable_pte(*spte)) {
                                rmap_remove(kvm, spte);
                                --kvm->stat.lpages;
                                __set_spte(spte, shadow_trap_nonpresent_pte);
@@ -787,7 +771,7 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
 
                        new_spte &= ~PT_WRITABLE_MASK;
                        new_spte &= ~SPTE_HOST_WRITEABLE;
-                       if (is_writeble_pte(*spte))
+                       if (is_writable_pte(*spte))
                                kvm_set_pfn_dirty(spte_to_pfn(*spte));
                        __set_spte(spte, new_spte);
                        spte = rmap_next(kvm, rmapp, spte);
@@ -805,35 +789,32 @@ static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
                                         unsigned long data))
 {
        int i, j;
+       int ret;
        int retval = 0;
+       struct kvm_memslots *slots;
 
-       /*
-        * If mmap_sem isn't taken, we can look the memslots with only
-        * the mmu_lock by skipping over the slots with userspace_addr == 0.
-        */
-       for (i = 0; i < kvm->nmemslots; i++) {
-               struct kvm_memory_slot *memslot = &kvm->memslots[i];
+       slots = rcu_dereference(kvm->memslots);
+
+       for (i = 0; i < slots->nmemslots; i++) {
+               struct kvm_memory_slot *memslot = &slots->memslots[i];
                unsigned long start = memslot->userspace_addr;
                unsigned long end;
 
-               /* mmu_lock protects userspace_addr */
-               if (!start)
-                       continue;
-
                end = start + (memslot->npages << PAGE_SHIFT);
                if (hva >= start && hva < end) {
                        gfn_t gfn_offset = (hva - start) >> PAGE_SHIFT;
 
-                       retval |= handler(kvm, &memslot->rmap[gfn_offset],
-                                         data);
+                       ret = handler(kvm, &memslot->rmap[gfn_offset], data);
 
                        for (j = 0; j < KVM_NR_PAGE_SIZES - 1; ++j) {
                                int idx = gfn_offset;
                                idx /= KVM_PAGES_PER_HPAGE(PT_DIRECTORY_LEVEL + j);
-                               retval |= handler(kvm,
+                               ret |= handler(kvm,
                                        &memslot->lpage_info[j][idx].rmap_pde,
                                        data);
                        }
+                       trace_kvm_age_page(hva, memslot, ret);
+                       retval |= ret;
                }
        }
 
@@ -856,9 +837,15 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
        u64 *spte;
        int young = 0;
 
-       /* always return old for EPT */
+       /*
+        * Emulate the accessed bit for EPT, by checking if this page has
+        * an EPT mapping, and clearing it if it does. On the next access,
+        * a new EPT mapping will be established.
+        * This has some overhead, but not as much as the cost of swapping
+        * out actively used pages or breaking up actively used hugepages.
+        */
        if (!shadow_accessed_mask)
-               return 0;
+               return kvm_unmap_rmapp(kvm, rmapp, data);
 
        spte = rmap_next(kvm, rmapp, NULL);
        while (spte) {
@@ -1615,7 +1602,7 @@ static void mmu_unshadow(struct kvm *kvm, gfn_t gfn)
 
 static void page_header_update_slot(struct kvm *kvm, void *pte, gfn_t gfn)
 {
-       int slot = memslot_id(kvm, gfn_to_memslot(kvm, gfn));
+       int slot = memslot_id(kvm, gfn);
        struct kvm_mmu_page *sp = page_header(__pa(pte));
 
        __set_bit(slot, sp->slot_bitmap);
@@ -1639,7 +1626,7 @@ struct page *gva_to_page(struct kvm_vcpu *vcpu, gva_t gva)
 {
        struct page *page;
 
-       gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gva);
+       gpa_t gpa = kvm_mmu_gva_to_gpa_read(vcpu, gva, NULL);
 
        if (gpa == UNMAPPED_GVA)
                return NULL;
@@ -1852,7 +1839,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                 * is responsibility of mmu_get_page / kvm_sync_page.
                 * Same reasoning can be applied to dirty page accounting.
                 */
-               if (!can_unsync && is_writeble_pte(*sptep))
+               if (!can_unsync && is_writable_pte(*sptep))
                        goto set_pte;
 
                if (mmu_need_write_protect(vcpu, gfn, can_unsync)) {
@@ -1860,7 +1847,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                                 __func__, gfn);
                        ret = 1;
                        pte_access &= ~ACC_WRITE_MASK;
-                       if (is_writeble_pte(spte))
+                       if (is_writable_pte(spte))
                                spte &= ~PT_WRITABLE_MASK;
                }
        }
@@ -1881,7 +1868,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                         bool reset_host_protection)
 {
        int was_rmapped = 0;
-       int was_writeble = is_writeble_pte(*sptep);
+       int was_writable = is_writable_pte(*sptep);
        int rmap_count;
 
        pgprintk("%s: spte %llx access %x write_fault %d"
@@ -1932,7 +1919,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                if (rmap_count > RMAP_RECYCLE_THRESHOLD)
                        rmap_recycle(vcpu, sptep, gfn);
        } else {
-               if (was_writeble)
+               if (was_writable)
                        kvm_release_pfn_dirty(pfn);
                else
                        kvm_release_pfn_clean(pfn);
@@ -2162,8 +2149,11 @@ void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu)
        spin_unlock(&vcpu->kvm->mmu_lock);
 }
 
-static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr)
+static gpa_t nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t vaddr,
+                                 u32 access, u32 *error)
 {
+       if (error)
+               *error = 0;
        return vaddr;
 }
 
@@ -2747,7 +2737,7 @@ int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva)
        if (tdp_enabled)
                return 0;
 
-       gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gva);
+       gpa = kvm_mmu_gva_to_gpa_read(vcpu, gva, NULL);
 
        spin_lock(&vcpu->kvm->mmu_lock);
        r = kvm_mmu_unprotect_page(vcpu->kvm, gpa >> PAGE_SHIFT);
@@ -2847,16 +2837,13 @@ static int alloc_mmu_pages(struct kvm_vcpu *vcpu)
         */
        page = alloc_page(GFP_KERNEL | __GFP_DMA32);
        if (!page)
-               goto error_1;
+               return -ENOMEM;
+
        vcpu->arch.mmu.pae_root = page_address(page);
        for (i = 0; i < 4; ++i)
                vcpu->arch.mmu.pae_root[i] = INVALID_PAGE;
 
        return 0;
-
-error_1:
-       free_mmu_pages(vcpu);
-       return -ENOMEM;
 }
 
 int kvm_mmu_create(struct kvm_vcpu *vcpu)
@@ -2936,10 +2923,9 @@ static int mmu_shrink(int nr_to_scan, gfp_t gfp_mask)
        spin_lock(&kvm_lock);
 
        list_for_each_entry(kvm, &vm_list, vm_list) {
-               int npages;
+               int npages, idx;
 
-               if (!down_read_trylock(&kvm->slots_lock))
-                       continue;
+               idx = srcu_read_lock(&kvm->srcu);
                spin_lock(&kvm->mmu_lock);
                npages = kvm->arch.n_alloc_mmu_pages -
                         kvm->arch.n_free_mmu_pages;
@@ -2952,7 +2938,7 @@ static int mmu_shrink(int nr_to_scan, gfp_t gfp_mask)
                nr_to_scan--;
 
                spin_unlock(&kvm->mmu_lock);
-               up_read(&kvm->slots_lock);
+               srcu_read_unlock(&kvm->srcu, idx);
        }
        if (kvm_freed)
                list_move_tail(&kvm_freed->vm_list, &vm_list);
@@ -3019,9 +3005,11 @@ unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm)
        int i;
        unsigned int nr_mmu_pages;
        unsigned int  nr_pages = 0;
+       struct kvm_memslots *slots;
 
-       for (i = 0; i < kvm->nmemslots; i++)
-               nr_pages += kvm->memslots[i].npages;
+       slots = rcu_dereference(kvm->memslots);
+       for (i = 0; i < slots->nmemslots; i++)
+               nr_pages += slots->memslots[i].npages;
 
        nr_mmu_pages = nr_pages * KVM_PERMILLE_MMU_PAGES / 1000;
        nr_mmu_pages = max(nr_mmu_pages,
@@ -3246,7 +3234,7 @@ static void audit_mappings_page(struct kvm_vcpu *vcpu, u64 page_pte,
                if (is_shadow_present_pte(ent) && !is_last_spte(ent, level))
                        audit_mappings_page(vcpu, ent, va, level - 1);
                else {
-                       gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, va);
+                       gpa_t gpa = kvm_mmu_gva_to_gpa_read(vcpu, va, NULL);
                        gfn_t gfn = gpa >> PAGE_SHIFT;
                        pfn_t pfn = gfn_to_pfn(vcpu->kvm, gfn);
                        hpa_t hpa = (hpa_t)pfn << PAGE_SHIFT;
@@ -3291,10 +3279,12 @@ static void audit_mappings(struct kvm_vcpu *vcpu)
 static int count_rmaps(struct kvm_vcpu *vcpu)
 {
        int nmaps = 0;
-       int i, j, k;
+       int i, j, k, idx;
 
+       idx = srcu_read_lock(&kvm->srcu);
+       slots = rcu_dereference(kvm->memslots);
        for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
-               struct kvm_memory_slot *m = &vcpu->kvm->memslots[i];
+               struct kvm_memory_slot *m = &slots->memslots[i];
                struct kvm_rmap_desc *d;
 
                for (j = 0; j < m->npages; ++j) {
@@ -3317,6 +3307,7 @@ static int count_rmaps(struct kvm_vcpu *vcpu)
                        }
                }
        }
+       srcu_read_unlock(&kvm->srcu, idx);
        return nmaps;
 }
 
index 61a1b3884b4954b1173cc5c21aced330acb2c938..be66759321a546aeee68a37cdc84c179ef39266e 100644 (file)
@@ -2,6 +2,7 @@
 #define __KVM_X86_MMU_H
 
 #include <linux/kvm_host.h>
+#include "kvm_cache_regs.h"
 
 #define PT64_PT_BITS 9
 #define PT64_ENT_PER_PAGE (1 << PT64_PT_BITS)
 #define PT32_ROOT_LEVEL 2
 #define PT32E_ROOT_LEVEL 3
 
+#define PT_PDPE_LEVEL 3
+#define PT_DIRECTORY_LEVEL 2
+#define PT_PAGE_TABLE_LEVEL 1
+
+#define PFERR_PRESENT_MASK (1U << 0)
+#define PFERR_WRITE_MASK (1U << 1)
+#define PFERR_USER_MASK (1U << 2)
+#define PFERR_RSVD_MASK (1U << 3)
+#define PFERR_FETCH_MASK (1U << 4)
+
 int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]);
 
 static inline void kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu)
@@ -53,30 +64,6 @@ static inline int kvm_mmu_reload(struct kvm_vcpu *vcpu)
        return kvm_mmu_load(vcpu);
 }
 
-static inline int is_long_mode(struct kvm_vcpu *vcpu)
-{
-#ifdef CONFIG_X86_64
-       return vcpu->arch.shadow_efer & EFER_LMA;
-#else
-       return 0;
-#endif
-}
-
-static inline int is_pae(struct kvm_vcpu *vcpu)
-{
-       return vcpu->arch.cr4 & X86_CR4_PAE;
-}
-
-static inline int is_pse(struct kvm_vcpu *vcpu)
-{
-       return vcpu->arch.cr4 & X86_CR4_PSE;
-}
-
-static inline int is_paging(struct kvm_vcpu *vcpu)
-{
-       return vcpu->arch.cr0 & X86_CR0_PG;
-}
-
 static inline int is_present_gpte(unsigned long pte)
 {
        return pte & PT_PRESENT_MASK;
index ede2131a9225eb00530aafb62962e383ab2a7101..81eab9a50e6afdbe07c8d264e7db6dedcdc5daed 100644 (file)
@@ -162,7 +162,7 @@ walk:
                if (rsvd_fault)
                        goto access_error;
 
-               if (write_fault && !is_writeble_pte(pte))
+               if (write_fault && !is_writable_pte(pte))
                        if (user_fault || is_write_protection(vcpu))
                                goto access_error;
 
@@ -490,18 +490,23 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
        spin_unlock(&vcpu->kvm->mmu_lock);
 }
 
-static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr)
+static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr, u32 access,
+                              u32 *error)
 {
        struct guest_walker walker;
        gpa_t gpa = UNMAPPED_GVA;
        int r;
 
-       r = FNAME(walk_addr)(&walker, vcpu, vaddr, 0, 0, 0);
+       r = FNAME(walk_addr)(&walker, vcpu, vaddr,
+                            !!(access & PFERR_WRITE_MASK),
+                            !!(access & PFERR_USER_MASK),
+                            !!(access & PFERR_FETCH_MASK));
 
        if (r) {
                gpa = gfn_to_gpa(walker.gfn);
                gpa |= vaddr & ~PAGE_MASK;
-       }
+       } else if (error)
+               *error = walker.error_code;
 
        return gpa;
 }
index 1d9b33843c80ef521dc059697285cfed06cfd7d7..52f78dd03010569eba75d6db749d83e4c19265e9 100644 (file)
@@ -231,7 +231,7 @@ static void svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
                efer &= ~EFER_LME;
 
        to_svm(vcpu)->vmcb->save.efer = efer | EFER_SVME;
-       vcpu->arch.shadow_efer = efer;
+       vcpu->arch.efer = efer;
 }
 
 static void svm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
@@ -540,6 +540,8 @@ static void init_vmcb(struct vcpu_svm *svm)
        struct vmcb_control_area *control = &svm->vmcb->control;
        struct vmcb_save_area *save = &svm->vmcb->save;
 
+       svm->vcpu.fpu_active = 1;
+
        control->intercept_cr_read =    INTERCEPT_CR0_MASK |
                                        INTERCEPT_CR3_MASK |
                                        INTERCEPT_CR4_MASK;
@@ -552,13 +554,19 @@ static void init_vmcb(struct vcpu_svm *svm)
        control->intercept_dr_read =    INTERCEPT_DR0_MASK |
                                        INTERCEPT_DR1_MASK |
                                        INTERCEPT_DR2_MASK |
-                                       INTERCEPT_DR3_MASK;
+                                       INTERCEPT_DR3_MASK |
+                                       INTERCEPT_DR4_MASK |
+                                       INTERCEPT_DR5_MASK |
+                                       INTERCEPT_DR6_MASK |
+                                       INTERCEPT_DR7_MASK;
 
        control->intercept_dr_write =   INTERCEPT_DR0_MASK |
                                        INTERCEPT_DR1_MASK |
                                        INTERCEPT_DR2_MASK |
                                        INTERCEPT_DR3_MASK |
+                                       INTERCEPT_DR4_MASK |
                                        INTERCEPT_DR5_MASK |
+                                       INTERCEPT_DR6_MASK |
                                        INTERCEPT_DR7_MASK;
 
        control->intercept_exceptions = (1 << PF_VECTOR) |
@@ -569,6 +577,7 @@ static void init_vmcb(struct vcpu_svm *svm)
        control->intercept =    (1ULL << INTERCEPT_INTR) |
                                (1ULL << INTERCEPT_NMI) |
                                (1ULL << INTERCEPT_SMI) |
+                               (1ULL << INTERCEPT_SELECTIVE_CR0) |
                                (1ULL << INTERCEPT_CPUID) |
                                (1ULL << INTERCEPT_INVD) |
                                (1ULL << INTERCEPT_HLT) |
@@ -641,10 +650,8 @@ static void init_vmcb(struct vcpu_svm *svm)
                control->intercept &= ~((1ULL << INTERCEPT_TASK_SWITCH) |
                                        (1ULL << INTERCEPT_INVLPG));
                control->intercept_exceptions &= ~(1 << PF_VECTOR);
-               control->intercept_cr_read &= ~(INTERCEPT_CR0_MASK|
-                                               INTERCEPT_CR3_MASK);
-               control->intercept_cr_write &= ~(INTERCEPT_CR0_MASK|
-                                                INTERCEPT_CR3_MASK);
+               control->intercept_cr_read &= ~INTERCEPT_CR3_MASK;
+               control->intercept_cr_write &= ~INTERCEPT_CR3_MASK;
                save->g_pat = 0x0007040600070406ULL;
                save->cr3 = 0;
                save->cr4 = 0;
@@ -730,7 +737,6 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
        init_vmcb(svm);
 
        fx_init(&svm->vcpu);
-       svm->vcpu.fpu_active = 1;
        svm->vcpu.arch.apic_base = 0xfee00000 | MSR_IA32_APICBASE_ENABLE;
        if (kvm_vcpu_is_bsp(&svm->vcpu))
                svm->vcpu.arch.apic_base |= MSR_IA32_APICBASE_BSP;
@@ -765,14 +771,16 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        if (unlikely(cpu != vcpu->cpu)) {
                u64 delta;
 
-               /*
-                * Make sure that the guest sees a monotonically
-                * increasing TSC.
-                */
-               delta = vcpu->arch.host_tsc - native_read_tsc();
-               svm->vmcb->control.tsc_offset += delta;
-               if (is_nested(svm))
-                       svm->nested.hsave->control.tsc_offset += delta;
+               if (check_tsc_unstable()) {
+                       /*
+                        * Make sure that the guest sees a monotonically
+                        * increasing TSC.
+                        */
+                       delta = vcpu->arch.host_tsc - native_read_tsc();
+                       svm->vmcb->control.tsc_offset += delta;
+                       if (is_nested(svm))
+                               svm->nested.hsave->control.tsc_offset += delta;
+               }
                vcpu->cpu = cpu;
                kvm_migrate_timers(vcpu);
                svm->asid_generation = 0;
@@ -954,42 +962,59 @@ static void svm_set_gdt(struct kvm_vcpu *vcpu, struct descriptor_table *dt)
        svm->vmcb->save.gdtr.base = dt->base ;
 }
 
+static void svm_decache_cr0_guest_bits(struct kvm_vcpu *vcpu)
+{
+}
+
 static void svm_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
 {
 }
 
+static void update_cr0_intercept(struct vcpu_svm *svm)
+{
+       ulong gcr0 = svm->vcpu.arch.cr0;
+       u64 *hcr0 = &svm->vmcb->save.cr0;
+
+       if (!svm->vcpu.fpu_active)
+               *hcr0 |= SVM_CR0_SELECTIVE_MASK;
+       else
+               *hcr0 = (*hcr0 & ~SVM_CR0_SELECTIVE_MASK)
+                       | (gcr0 & SVM_CR0_SELECTIVE_MASK);
+
+
+       if (gcr0 == *hcr0 && svm->vcpu.fpu_active) {
+               svm->vmcb->control.intercept_cr_read &= ~INTERCEPT_CR0_MASK;
+               svm->vmcb->control.intercept_cr_write &= ~INTERCEPT_CR0_MASK;
+       } else {
+               svm->vmcb->control.intercept_cr_read |= INTERCEPT_CR0_MASK;
+               svm->vmcb->control.intercept_cr_write |= INTERCEPT_CR0_MASK;
+       }
+}
+
 static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
 #ifdef CONFIG_X86_64
-       if (vcpu->arch.shadow_efer & EFER_LME) {
+       if (vcpu->arch.efer & EFER_LME) {
                if (!is_paging(vcpu) && (cr0 & X86_CR0_PG)) {
-                       vcpu->arch.shadow_efer |= EFER_LMA;
+                       vcpu->arch.efer |= EFER_LMA;
                        svm->vmcb->save.efer |= EFER_LMA | EFER_LME;
                }
 
                if (is_paging(vcpu) && !(cr0 & X86_CR0_PG)) {
-                       vcpu->arch.shadow_efer &= ~EFER_LMA;
+                       vcpu->arch.efer &= ~EFER_LMA;
                        svm->vmcb->save.efer &= ~(EFER_LMA | EFER_LME);
                }
        }
 #endif
-       if (npt_enabled)
-               goto set;
+       vcpu->arch.cr0 = cr0;
 
-       if ((vcpu->arch.cr0 & X86_CR0_TS) && !(cr0 & X86_CR0_TS)) {
-               svm->vmcb->control.intercept_exceptions &= ~(1 << NM_VECTOR);
-               vcpu->fpu_active = 1;
-       }
+       if (!npt_enabled)
+               cr0 |= X86_CR0_PG | X86_CR0_WP;
 
-       vcpu->arch.cr0 = cr0;
-       cr0 |= X86_CR0_PG | X86_CR0_WP;
-       if (!vcpu->fpu_active) {
-               svm->vmcb->control.intercept_exceptions |= (1 << NM_VECTOR);
+       if (!vcpu->fpu_active)
                cr0 |= X86_CR0_TS;
-       }
-set:
        /*
         * re-enable caching here because the QEMU bios
         * does not do it - this results in some delay at
@@ -997,6 +1022,7 @@ set:
         */
        cr0 &= ~(X86_CR0_CD | X86_CR0_NW);
        svm->vmcb->save.cr0 = cr0;
+       update_cr0_intercept(svm);
 }
 
 static void svm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
@@ -1102,76 +1128,70 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *sd)
        svm->vmcb->control.asid = sd->next_asid++;
 }
 
-static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
+static int svm_get_dr(struct kvm_vcpu *vcpu, int dr, unsigned long *dest)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
-       unsigned long val;
 
        switch (dr) {
        case 0 ... 3:
-               val = vcpu->arch.db[dr];
+               *dest = vcpu->arch.db[dr];
                break;
+       case 4:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 6:
                if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
-                       val = vcpu->arch.dr6;
+                       *dest = vcpu->arch.dr6;
                else
-                       val = svm->vmcb->save.dr6;
+                       *dest = svm->vmcb->save.dr6;
                break;
+       case 5:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 7:
                if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
-                       val = vcpu->arch.dr7;
+                       *dest = vcpu->arch.dr7;
                else
-                       val = svm->vmcb->save.dr7;
+                       *dest = svm->vmcb->save.dr7;
                break;
-       default:
-               val = 0;
        }
 
-       return val;
+       return EMULATE_DONE;
 }
 
-static void svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value,
-                      int *exception)
+static int svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
-       *exception = 0;
-
        switch (dr) {
        case 0 ... 3:
                vcpu->arch.db[dr] = value;
                if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
                        vcpu->arch.eff_db[dr] = value;
-               return;
-       case 4 ... 5:
-               if (vcpu->arch.cr4 & X86_CR4_DE)
-                       *exception = UD_VECTOR;
-               return;
+               break;
+       case 4:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 6:
-               if (value & 0xffffffff00000000ULL) {
-                       *exception = GP_VECTOR;
-                       return;
-               }
                vcpu->arch.dr6 = (value & DR6_VOLATILE) | DR6_FIXED_1;
-               return;
+               break;
+       case 5:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 7:
-               if (value & 0xffffffff00000000ULL) {
-                       *exception = GP_VECTOR;
-                       return;
-               }
                vcpu->arch.dr7 = (value & DR7_VOLATILE) | DR7_FIXED_1;
                if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) {
                        svm->vmcb->save.dr7 = vcpu->arch.dr7;
                        vcpu->arch.switch_db_regs = (value & DR7_BP_EN_MASK);
                }
-               return;
-       default:
-               /* FIXME: Possible case? */
-               printk(KERN_DEBUG "%s: unexpected dr %u\n",
-                      __func__, dr);
-               *exception = UD_VECTOR;
-               return;
+               break;
        }
+
+       return EMULATE_DONE;
 }
 
 static int pf_interception(struct vcpu_svm *svm)
@@ -1239,13 +1259,17 @@ static int ud_interception(struct vcpu_svm *svm)
        return 1;
 }
 
-static int nm_interception(struct vcpu_svm *svm)
+static void svm_fpu_activate(struct kvm_vcpu *vcpu)
 {
+       struct vcpu_svm *svm = to_svm(vcpu);
        svm->vmcb->control.intercept_exceptions &= ~(1 << NM_VECTOR);
-       if (!(svm->vcpu.arch.cr0 & X86_CR0_TS))
-               svm->vmcb->save.cr0 &= ~X86_CR0_TS;
        svm->vcpu.fpu_active = 1;
+       update_cr0_intercept(svm);
+}
 
+static int nm_interception(struct vcpu_svm *svm)
+{
+       svm_fpu_activate(&svm->vcpu);
        return 1;
 }
 
@@ -1337,7 +1361,7 @@ static int vmmcall_interception(struct vcpu_svm *svm)
 
 static int nested_svm_check_permissions(struct vcpu_svm *svm)
 {
-       if (!(svm->vcpu.arch.shadow_efer & EFER_SVME)
+       if (!(svm->vcpu.arch.efer & EFER_SVME)
            || !is_paging(&svm->vcpu)) {
                kvm_queue_exception(&svm->vcpu, UD_VECTOR);
                return 1;
@@ -1740,8 +1764,8 @@ static bool nested_svm_vmrun(struct vcpu_svm *svm)
        hsave->save.ds     = vmcb->save.ds;
        hsave->save.gdtr   = vmcb->save.gdtr;
        hsave->save.idtr   = vmcb->save.idtr;
-       hsave->save.efer   = svm->vcpu.arch.shadow_efer;
-       hsave->save.cr0    = svm->vcpu.arch.cr0;
+       hsave->save.efer   = svm->vcpu.arch.efer;
+       hsave->save.cr0    = kvm_read_cr0(&svm->vcpu);
        hsave->save.cr4    = svm->vcpu.arch.cr4;
        hsave->save.rflags = vmcb->save.rflags;
        hsave->save.rip    = svm->next_rip;
@@ -2153,9 +2177,10 @@ static int rdmsr_interception(struct vcpu_svm *svm)
        u32 ecx = svm->vcpu.arch.regs[VCPU_REGS_RCX];
        u64 data;
 
-       if (svm_get_msr(&svm->vcpu, ecx, &data))
+       if (svm_get_msr(&svm->vcpu, ecx, &data)) {
+               trace_kvm_msr_read_ex(ecx);
                kvm_inject_gp(&svm->vcpu, 0);
-       else {
+       else {
                trace_kvm_msr_read(ecx, data);
 
                svm->vcpu.arch.regs[VCPU_REGS_RAX] = data & 0xffffffff;
@@ -2247,13 +2272,15 @@ static int wrmsr_interception(struct vcpu_svm *svm)
        u64 data = (svm->vcpu.arch.regs[VCPU_REGS_RAX] & -1u)
                | ((u64)(svm->vcpu.arch.regs[VCPU_REGS_RDX] & -1u) << 32);
 
-       trace_kvm_msr_write(ecx, data);
 
        svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
-       if (svm_set_msr(&svm->vcpu, ecx, data))
+       if (svm_set_msr(&svm->vcpu, ecx, data)) {
+               trace_kvm_msr_write_ex(ecx, data);
                kvm_inject_gp(&svm->vcpu, 0);
-       else
+       } else {
+               trace_kvm_msr_write(ecx, data);
                skip_emulated_instruction(&svm->vcpu);
+       }
        return 1;
 }
 
@@ -2297,7 +2324,7 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_READ_CR3]                     = emulate_on_interception,
        [SVM_EXIT_READ_CR4]                     = emulate_on_interception,
        [SVM_EXIT_READ_CR8]                     = emulate_on_interception,
-       /* for now: */
+       [SVM_EXIT_CR0_SEL_WRITE]                = emulate_on_interception,
        [SVM_EXIT_WRITE_CR0]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_CR3]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_CR4]                    = emulate_on_interception,
@@ -2306,11 +2333,17 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_READ_DR1]                     = emulate_on_interception,
        [SVM_EXIT_READ_DR2]                     = emulate_on_interception,
        [SVM_EXIT_READ_DR3]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR4]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR5]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR6]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR7]                     = emulate_on_interception,
        [SVM_EXIT_WRITE_DR0]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR1]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR2]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR3]                    = emulate_on_interception,
+       [SVM_EXIT_WRITE_DR4]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR5]                    = emulate_on_interception,
+       [SVM_EXIT_WRITE_DR6]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR7]                    = emulate_on_interception,
        [SVM_EXIT_EXCP_BASE + DB_VECTOR]        = db_interception,
        [SVM_EXIT_EXCP_BASE + BP_VECTOR]        = bp_interception,
@@ -2383,20 +2416,10 @@ static int handle_exit(struct kvm_vcpu *vcpu)
 
        svm_complete_interrupts(svm);
 
-       if (npt_enabled) {
-               int mmu_reload = 0;
-               if ((vcpu->arch.cr0 ^ svm->vmcb->save.cr0) & X86_CR0_PG) {
-                       svm_set_cr0(vcpu, svm->vmcb->save.cr0);
-                       mmu_reload = 1;
-               }
+       if (!(svm->vmcb->control.intercept_cr_write & INTERCEPT_CR0_MASK))
                vcpu->arch.cr0 = svm->vmcb->save.cr0;
+       if (npt_enabled)
                vcpu->arch.cr3 = svm->vmcb->save.cr3;
-               if (mmu_reload) {
-                       kvm_mmu_reset_context(vcpu);
-                       kvm_mmu_load(vcpu);
-               }
-       }
-
 
        if (svm->vmcb->control.exit_code == SVM_EXIT_ERR) {
                kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY;
@@ -2798,12 +2821,6 @@ static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
 
        svm->vmcb->save.cr3 = root;
        force_new_asid(vcpu);
-
-       if (vcpu->fpu_active) {
-               svm->vmcb->control.intercept_exceptions |= (1 << NM_VECTOR);
-               svm->vmcb->save.cr0 |= X86_CR0_TS;
-               vcpu->fpu_active = 0;
-       }
 }
 
 static int is_disabled(void)
@@ -2852,6 +2869,10 @@ static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
        return 0;
 }
 
+static void svm_cpuid_update(struct kvm_vcpu *vcpu)
+{
+}
+
 static const struct trace_print_flags svm_exit_reasons_str[] = {
        { SVM_EXIT_READ_CR0,                    "read_cr0" },
        { SVM_EXIT_READ_CR3,                    "read_cr3" },
@@ -2905,9 +2926,22 @@ static const struct trace_print_flags svm_exit_reasons_str[] = {
        { -1, NULL }
 };
 
-static bool svm_gb_page_enable(void)
+static int svm_get_lpage_level(void)
 {
-       return true;
+       return PT_PDPE_LEVEL;
+}
+
+static bool svm_rdtscp_supported(void)
+{
+       return false;
+}
+
+static void svm_fpu_deactivate(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       update_cr0_intercept(svm);
+       svm->vmcb->control.intercept_exceptions |= 1 << NM_VECTOR;
 }
 
 static struct kvm_x86_ops svm_x86_ops = {
@@ -2936,6 +2970,7 @@ static struct kvm_x86_ops svm_x86_ops = {
        .set_segment = svm_set_segment,
        .get_cpl = svm_get_cpl,
        .get_cs_db_l_bits = kvm_get_cs_db_l_bits,
+       .decache_cr0_guest_bits = svm_decache_cr0_guest_bits,
        .decache_cr4_guest_bits = svm_decache_cr4_guest_bits,
        .set_cr0 = svm_set_cr0,
        .set_cr3 = svm_set_cr3,
@@ -2950,6 +2985,8 @@ static struct kvm_x86_ops svm_x86_ops = {
        .cache_reg = svm_cache_reg,
        .get_rflags = svm_get_rflags,
        .set_rflags = svm_set_rflags,
+       .fpu_activate = svm_fpu_activate,
+       .fpu_deactivate = svm_fpu_deactivate,
 
        .tlb_flush = svm_flush_tlb,
 
@@ -2975,7 +3012,11 @@ static struct kvm_x86_ops svm_x86_ops = {
        .get_mt_mask = svm_get_mt_mask,
 
        .exit_reasons_str = svm_exit_reasons_str,
-       .gb_page_enable = svm_gb_page_enable,
+       .get_lpage_level = svm_get_lpage_level,
+
+       .cpuid_update = svm_cpuid_update,
+
+       .rdtscp_supported = svm_rdtscp_supported,
 };
 
 static int __init svm_init(void)
index 816e0449db0baa19fd48638a6fe5d49d337d1e74..6ad30a29f04416fd65c2fc41f43e514f8a7c0004 100644 (file)
@@ -55,6 +55,38 @@ TRACE_EVENT(kvm_hypercall,
                 __entry->a3)
 );
 
+/*
+ * Tracepoint for hypercall.
+ */
+TRACE_EVENT(kvm_hv_hypercall,
+       TP_PROTO(__u16 code, bool fast, __u16 rep_cnt, __u16 rep_idx,
+                __u64 ingpa, __u64 outgpa),
+       TP_ARGS(code, fast, rep_cnt, rep_idx, ingpa, outgpa),
+
+       TP_STRUCT__entry(
+               __field(        __u16,          code            )
+               __field(        bool,           fast            )
+               __field(        __u16,          rep_cnt         )
+               __field(        __u16,          rep_idx         )
+               __field(        __u64,          ingpa           )
+               __field(        __u64,          outgpa          )
+       ),
+
+       TP_fast_assign(
+               __entry->code           = code;
+               __entry->fast           = fast;
+               __entry->rep_cnt        = rep_cnt;
+               __entry->rep_idx        = rep_idx;
+               __entry->ingpa          = ingpa;
+               __entry->outgpa         = outgpa;
+       ),
+
+       TP_printk("code 0x%x %s cnt 0x%x idx 0x%x in 0x%llx out 0x%llx",
+                 __entry->code, __entry->fast ? "fast" : "slow",
+                 __entry->rep_cnt, __entry->rep_idx,  __entry->ingpa,
+                 __entry->outgpa)
+);
+
 /*
  * Tracepoint for PIO.
  */
@@ -214,28 +246,33 @@ TRACE_EVENT(kvm_page_fault,
  * Tracepoint for guest MSR access.
  */
 TRACE_EVENT(kvm_msr,
-       TP_PROTO(unsigned int rw, unsigned int ecx, unsigned long data),
-       TP_ARGS(rw, ecx, data),
+       TP_PROTO(unsigned write, u32 ecx, u64 data, bool exception),
+       TP_ARGS(write, ecx, data, exception),
 
        TP_STRUCT__entry(
-               __field(        unsigned int,   rw              )
-               __field(        unsigned int,   ecx             )
-               __field(        unsigned long,  data            )
+               __field(        unsigned,       write           )
+               __field(        u32,            ecx             )
+               __field(        u64,            data            )
+               __field(        u8,             exception       )
        ),
 
        TP_fast_assign(
-               __entry->rw             = rw;
+               __entry->write          = write;
                __entry->ecx            = ecx;
                __entry->data           = data;
+               __entry->exception      = exception;
        ),
 
-       TP_printk("msr_%s %x = 0x%lx",
-                 __entry->rw ? "write" : "read",
-                 __entry->ecx, __entry->data)
+       TP_printk("msr_%s %x = 0x%llx%s",
+                 __entry->write ? "write" : "read",
+                 __entry->ecx, __entry->data,
+                 __entry->exception ? " (#GP)" : "")
 );
 
-#define trace_kvm_msr_read(ecx, data)          trace_kvm_msr(0, ecx, data)
-#define trace_kvm_msr_write(ecx, data)         trace_kvm_msr(1, ecx, data)
+#define trace_kvm_msr_read(ecx, data)      trace_kvm_msr(0, ecx, data, false)
+#define trace_kvm_msr_write(ecx, data)     trace_kvm_msr(1, ecx, data, false)
+#define trace_kvm_msr_read_ex(ecx)         trace_kvm_msr(0, ecx, 0, true)
+#define trace_kvm_msr_write_ex(ecx, data)  trace_kvm_msr(1, ecx, data, true)
 
 /*
  * Tracepoint for guest CR access.
index d4918d6fc9244207b20705ad74e2e620b3891b56..14873b9f84308ad125ae12969e0eeed269705de4 100644 (file)
@@ -61,6 +61,21 @@ module_param_named(unrestricted_guest,
 static int __read_mostly emulate_invalid_guest_state = 0;
 module_param(emulate_invalid_guest_state, bool, S_IRUGO);
 
+#define KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST                          \
+       (X86_CR0_WP | X86_CR0_NE | X86_CR0_NW | X86_CR0_CD)
+#define KVM_GUEST_CR0_MASK                                             \
+       (KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE)
+#define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST                                \
+       (X86_CR0_WP | X86_CR0_NE)
+#define KVM_VM_CR0_ALWAYS_ON                                           \
+       (KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE)
+#define KVM_CR4_GUEST_OWNED_BITS                                     \
+       (X86_CR4_PVI | X86_CR4_DE | X86_CR4_PCE | X86_CR4_OSFXSR      \
+        | X86_CR4_OSXMMEXCPT)
+
+#define KVM_PMODE_VM_CR4_ALWAYS_ON (X86_CR4_PAE | X86_CR4_VMXE)
+#define KVM_RMODE_VM_CR4_ALWAYS_ON (X86_CR4_VME | X86_CR4_PAE | X86_CR4_VMXE)
+
 /*
  * These 2 parameters are used to config the controls for Pause-Loop Exiting:
  * ple_gap:    upper bound on the amount of time between two successive
@@ -136,6 +151,8 @@ struct vcpu_vmx {
        ktime_t entry_time;
        s64 vnmi_blocked_time;
        u32 exit_reason;
+
+       bool rdtscp_enabled;
 };
 
 static inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu)
@@ -210,7 +227,7 @@ static const u32 vmx_msr_index[] = {
 #ifdef CONFIG_X86_64
        MSR_SYSCALL_MASK, MSR_LSTAR, MSR_CSTAR,
 #endif
-       MSR_EFER, MSR_K6_STAR,
+       MSR_EFER, MSR_TSC_AUX, MSR_K6_STAR,
 };
 #define NR_VMX_MSR ARRAY_SIZE(vmx_msr_index)
 
@@ -301,6 +318,11 @@ static inline bool cpu_has_vmx_ept_2m_page(void)
        return !!(vmx_capability.ept & VMX_EPT_2MB_PAGE_BIT);
 }
 
+static inline bool cpu_has_vmx_ept_1g_page(void)
+{
+       return !!(vmx_capability.ept & VMX_EPT_1GB_PAGE_BIT);
+}
+
 static inline int cpu_has_vmx_invept_individual_addr(void)
 {
        return !!(vmx_capability.ept & VMX_EPT_EXTENT_INDIVIDUAL_BIT);
@@ -336,9 +358,7 @@ static inline int cpu_has_vmx_ple(void)
 
 static inline int vm_need_virtualize_apic_accesses(struct kvm *kvm)
 {
-       return flexpriority_enabled &&
-               (cpu_has_vmx_virtualize_apic_accesses()) &&
-               (irqchip_in_kernel(kvm));
+       return flexpriority_enabled && irqchip_in_kernel(kvm);
 }
 
 static inline int cpu_has_vmx_vpid(void)
@@ -347,6 +367,12 @@ static inline int cpu_has_vmx_vpid(void)
                SECONDARY_EXEC_ENABLE_VPID;
 }
 
+static inline int cpu_has_vmx_rdtscp(void)
+{
+       return vmcs_config.cpu_based_2nd_exec_ctrl &
+               SECONDARY_EXEC_RDTSCP;
+}
+
 static inline int cpu_has_virtual_nmis(void)
 {
        return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS;
@@ -551,22 +577,18 @@ static void update_exception_bitmap(struct kvm_vcpu *vcpu)
 {
        u32 eb;
 
-       eb = (1u << PF_VECTOR) | (1u << UD_VECTOR) | (1u << MC_VECTOR);
-       if (!vcpu->fpu_active)
-               eb |= 1u << NM_VECTOR;
-       /*
-        * Unconditionally intercept #DB so we can maintain dr6 without
-        * reading it every exit.
-        */
-       eb |= 1u << DB_VECTOR;
-       if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) {
-               if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
-                       eb |= 1u << BP_VECTOR;
-       }
+       eb = (1u << PF_VECTOR) | (1u << UD_VECTOR) | (1u << MC_VECTOR) |
+            (1u << NM_VECTOR) | (1u << DB_VECTOR);
+       if ((vcpu->guest_debug &
+            (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP)) ==
+           (KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP))
+               eb |= 1u << BP_VECTOR;
        if (to_vmx(vcpu)->rmode.vm86_active)
                eb = ~0;
        if (enable_ept)
                eb &= ~(1u << PF_VECTOR); /* bypass_guest_pf = 0 */
+       if (vcpu->fpu_active)
+               eb &= ~(1u << NM_VECTOR);
        vmcs_write32(EXCEPTION_BITMAP, eb);
 }
 
@@ -589,7 +611,7 @@ static bool update_transition_efer(struct vcpu_vmx *vmx, int efer_offset)
        u64 guest_efer;
        u64 ignore_bits;
 
-       guest_efer = vmx->vcpu.arch.shadow_efer;
+       guest_efer = vmx->vcpu.arch.efer;
 
        /*
         * NX is emulated; LMA and LME handled by hardware; SCE meaninless
@@ -767,22 +789,30 @@ static void vmx_vcpu_put(struct kvm_vcpu *vcpu)
 
 static void vmx_fpu_activate(struct kvm_vcpu *vcpu)
 {
+       ulong cr0;
+
        if (vcpu->fpu_active)
                return;
        vcpu->fpu_active = 1;
-       vmcs_clear_bits(GUEST_CR0, X86_CR0_TS);
-       if (vcpu->arch.cr0 & X86_CR0_TS)
-               vmcs_set_bits(GUEST_CR0, X86_CR0_TS);
+       cr0 = vmcs_readl(GUEST_CR0);
+       cr0 &= ~(X86_CR0_TS | X86_CR0_MP);
+       cr0 |= kvm_read_cr0_bits(vcpu, X86_CR0_TS | X86_CR0_MP);
+       vmcs_writel(GUEST_CR0, cr0);
        update_exception_bitmap(vcpu);
+       vcpu->arch.cr0_guest_owned_bits = X86_CR0_TS;
+       vmcs_writel(CR0_GUEST_HOST_MASK, ~vcpu->arch.cr0_guest_owned_bits);
 }
 
+static void vmx_decache_cr0_guest_bits(struct kvm_vcpu *vcpu);
+
 static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu)
 {
-       if (!vcpu->fpu_active)
-               return;
-       vcpu->fpu_active = 0;
-       vmcs_set_bits(GUEST_CR0, X86_CR0_TS);
+       vmx_decache_cr0_guest_bits(vcpu);
+       vmcs_set_bits(GUEST_CR0, X86_CR0_TS | X86_CR0_MP);
        update_exception_bitmap(vcpu);
+       vcpu->arch.cr0_guest_owned_bits = 0;
+       vmcs_writel(CR0_GUEST_HOST_MASK, ~vcpu->arch.cr0_guest_owned_bits);
+       vmcs_writel(CR0_READ_SHADOW, vcpu->arch.cr0);
 }
 
 static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu)
@@ -878,6 +908,11 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
        vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
 }
 
+static bool vmx_rdtscp_supported(void)
+{
+       return cpu_has_vmx_rdtscp();
+}
+
 /*
  * Swap MSR entry in host/guest MSR entry array.
  */
@@ -913,12 +948,15 @@ static void setup_msrs(struct vcpu_vmx *vmx)
                index = __find_msr_index(vmx, MSR_CSTAR);
                if (index >= 0)
                        move_msr_up(vmx, index, save_nmsrs++);
+               index = __find_msr_index(vmx, MSR_TSC_AUX);
+               if (index >= 0 && vmx->rdtscp_enabled)
+                       move_msr_up(vmx, index, save_nmsrs++);
                /*
                 * MSR_K6_STAR is only needed on long mode guests, and only
                 * if efer.sce is enabled.
                 */
                index = __find_msr_index(vmx, MSR_K6_STAR);
-               if ((index >= 0) && (vmx->vcpu.arch.shadow_efer & EFER_SCE))
+               if ((index >= 0) && (vmx->vcpu.arch.efer & EFER_SCE))
                        move_msr_up(vmx, index, save_nmsrs++);
        }
 #endif
@@ -1002,6 +1040,10 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
        case MSR_IA32_SYSENTER_ESP:
                data = vmcs_readl(GUEST_SYSENTER_ESP);
                break;
+       case MSR_TSC_AUX:
+               if (!to_vmx(vcpu)->rdtscp_enabled)
+                       return 1;
+               /* Otherwise falls through */
        default:
                vmx_load_host_state(to_vmx(vcpu));
                msr = find_msr_entry(to_vmx(vcpu), msr_index);
@@ -1065,7 +1107,15 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
                        vcpu->arch.pat = data;
                        break;
                }
-               /* Otherwise falls through to kvm_set_msr_common */
+               ret = kvm_set_msr_common(vcpu, msr_index, data);
+               break;
+       case MSR_TSC_AUX:
+               if (!vmx->rdtscp_enabled)
+                       return 1;
+               /* Check reserved bit, higher 32 bits should be zero */
+               if ((data >> 32) != 0)
+                       return 1;
+               /* Otherwise falls through */
        default:
                msr = find_msr_entry(vmx, msr_index);
                if (msr) {
@@ -1224,6 +1274,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
              CPU_BASED_USE_IO_BITMAPS |
              CPU_BASED_MOV_DR_EXITING |
              CPU_BASED_USE_TSC_OFFSETING |
+             CPU_BASED_MWAIT_EXITING |
+             CPU_BASED_MONITOR_EXITING |
              CPU_BASED_INVLPG_EXITING;
        opt = CPU_BASED_TPR_SHADOW |
              CPU_BASED_USE_MSR_BITMAPS |
@@ -1243,7 +1295,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
                        SECONDARY_EXEC_ENABLE_VPID |
                        SECONDARY_EXEC_ENABLE_EPT |
                        SECONDARY_EXEC_UNRESTRICTED_GUEST |
-                       SECONDARY_EXEC_PAUSE_LOOP_EXITING;
+                       SECONDARY_EXEC_PAUSE_LOOP_EXITING |
+                       SECONDARY_EXEC_RDTSCP;
                if (adjust_vmx_controls(min2, opt2,
                                        MSR_IA32_VMX_PROCBASED_CTLS2,
                                        &_cpu_based_2nd_exec_control) < 0)
@@ -1457,8 +1510,12 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
 static gva_t rmode_tss_base(struct kvm *kvm)
 {
        if (!kvm->arch.tss_addr) {
-               gfn_t base_gfn = kvm->memslots[0].base_gfn +
-                                kvm->memslots[0].npages - 3;
+               struct kvm_memslots *slots;
+               gfn_t base_gfn;
+
+               slots = rcu_dereference(kvm->memslots);
+               base_gfn = kvm->memslots->memslots[0].base_gfn +
+                                kvm->memslots->memslots[0].npages - 3;
                return base_gfn << PAGE_SHIFT;
        }
        return kvm->arch.tss_addr;
@@ -1544,9 +1601,7 @@ static void vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)
         * of this msr depends on is_long_mode().
         */
        vmx_load_host_state(to_vmx(vcpu));
-       vcpu->arch.shadow_efer = efer;
-       if (!msr)
-               return;
+       vcpu->arch.efer = efer;
        if (efer & EFER_LMA) {
                vmcs_write32(VM_ENTRY_CONTROLS,
                             vmcs_read32(VM_ENTRY_CONTROLS) |
@@ -1576,13 +1631,13 @@ static void enter_lmode(struct kvm_vcpu *vcpu)
                             (guest_tr_ar & ~AR_TYPE_MASK)
                             | AR_TYPE_BUSY_64_TSS);
        }
-       vcpu->arch.shadow_efer |= EFER_LMA;
-       vmx_set_efer(vcpu, vcpu->arch.shadow_efer);
+       vcpu->arch.efer |= EFER_LMA;
+       vmx_set_efer(vcpu, vcpu->arch.efer);
 }
 
 static void exit_lmode(struct kvm_vcpu *vcpu)
 {
-       vcpu->arch.shadow_efer &= ~EFER_LMA;
+       vcpu->arch.efer &= ~EFER_LMA;
 
        vmcs_write32(VM_ENTRY_CONTROLS,
                     vmcs_read32(VM_ENTRY_CONTROLS)
@@ -1598,10 +1653,20 @@ static void vmx_flush_tlb(struct kvm_vcpu *vcpu)
                ept_sync_context(construct_eptp(vcpu->arch.mmu.root_hpa));
 }
 
+static void vmx_decache_cr0_guest_bits(struct kvm_vcpu *vcpu)
+{
+       ulong cr0_guest_owned_bits = vcpu->arch.cr0_guest_owned_bits;
+
+       vcpu->arch.cr0 &= ~cr0_guest_owned_bits;
+       vcpu->arch.cr0 |= vmcs_readl(GUEST_CR0) & cr0_guest_owned_bits;
+}
+
 static void vmx_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
 {
-       vcpu->arch.cr4 &= KVM_GUEST_CR4_MASK;
-       vcpu->arch.cr4 |= vmcs_readl(GUEST_CR4) & ~KVM_GUEST_CR4_MASK;
+       ulong cr4_guest_owned_bits = vcpu->arch.cr4_guest_owned_bits;
+
+       vcpu->arch.cr4 &= ~cr4_guest_owned_bits;
+       vcpu->arch.cr4 |= vmcs_readl(GUEST_CR4) & cr4_guest_owned_bits;
 }
 
 static void ept_load_pdptrs(struct kvm_vcpu *vcpu)
@@ -1646,7 +1711,7 @@ static void ept_update_paging_mode_cr0(unsigned long *hw_cr0,
                             (CPU_BASED_CR3_LOAD_EXITING |
                              CPU_BASED_CR3_STORE_EXITING));
                vcpu->arch.cr0 = cr0;
-               vmx_set_cr4(vcpu, vcpu->arch.cr4);
+               vmx_set_cr4(vcpu, kvm_read_cr4(vcpu));
        } else if (!is_paging(vcpu)) {
                /* From nonpaging to paging */
                vmcs_write32(CPU_BASED_VM_EXEC_CONTROL,
@@ -1654,23 +1719,13 @@ static void ept_update_paging_mode_cr0(unsigned long *hw_cr0,
                             ~(CPU_BASED_CR3_LOAD_EXITING |
                               CPU_BASED_CR3_STORE_EXITING));
                vcpu->arch.cr0 = cr0;
-               vmx_set_cr4(vcpu, vcpu->arch.cr4);
+               vmx_set_cr4(vcpu, kvm_read_cr4(vcpu));
        }
 
        if (!(cr0 & X86_CR0_WP))
                *hw_cr0 &= ~X86_CR0_WP;
 }
 
-static void ept_update_paging_mode_cr4(unsigned long *hw_cr4,
-                                       struct kvm_vcpu *vcpu)
-{
-       if (!is_paging(vcpu)) {
-               *hw_cr4 &= ~X86_CR4_PAE;
-               *hw_cr4 |= X86_CR4_PSE;
-       } else if (!(vcpu->arch.cr4 & X86_CR4_PAE))
-               *hw_cr4 &= ~X86_CR4_PAE;
-}
-
 static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -1682,8 +1737,6 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
        else
                hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK) | KVM_VM_CR0_ALWAYS_ON;
 
-       vmx_fpu_deactivate(vcpu);
-
        if (vmx->rmode.vm86_active && (cr0 & X86_CR0_PE))
                enter_pmode(vcpu);
 
@@ -1691,7 +1744,7 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
                enter_rmode(vcpu);
 
 #ifdef CONFIG_X86_64
-       if (vcpu->arch.shadow_efer & EFER_LME) {
+       if (vcpu->arch.efer & EFER_LME) {
                if (!is_paging(vcpu) && (cr0 & X86_CR0_PG))
                        enter_lmode(vcpu);
                if (is_paging(vcpu) && !(cr0 & X86_CR0_PG))
@@ -1702,12 +1755,12 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
        if (enable_ept)
                ept_update_paging_mode_cr0(&hw_cr0, cr0, vcpu);
 
+       if (!vcpu->fpu_active)
+               hw_cr0 |= X86_CR0_TS | X86_CR0_MP;
+
        vmcs_writel(CR0_READ_SHADOW, cr0);
        vmcs_writel(GUEST_CR0, hw_cr0);
        vcpu->arch.cr0 = cr0;
-
-       if (!(cr0 & X86_CR0_TS) || !(cr0 & X86_CR0_PE))
-               vmx_fpu_activate(vcpu);
 }
 
 static u64 construct_eptp(unsigned long root_hpa)
@@ -1738,8 +1791,6 @@ static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
 
        vmx_flush_tlb(vcpu);
        vmcs_writel(GUEST_CR3, guest_cr3);
-       if (vcpu->arch.cr0 & X86_CR0_PE)
-               vmx_fpu_deactivate(vcpu);
 }
 
 static void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
@@ -1748,8 +1799,14 @@ static void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
                    KVM_RMODE_VM_CR4_ALWAYS_ON : KVM_PMODE_VM_CR4_ALWAYS_ON);
 
        vcpu->arch.cr4 = cr4;
-       if (enable_ept)
-               ept_update_paging_mode_cr4(&hw_cr4, vcpu);
+       if (enable_ept) {
+               if (!is_paging(vcpu)) {
+                       hw_cr4 &= ~X86_CR4_PAE;
+                       hw_cr4 |= X86_CR4_PSE;
+               } else if (!(cr4 & X86_CR4_PAE)) {
+                       hw_cr4 &= ~X86_CR4_PAE;
+               }
+       }
 
        vmcs_writel(CR4_READ_SHADOW, cr4);
        vmcs_writel(GUEST_CR4, hw_cr4);
@@ -1787,7 +1844,7 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
 
 static int vmx_get_cpl(struct kvm_vcpu *vcpu)
 {
-       if (!(vcpu->arch.cr0 & X86_CR0_PE)) /* if real mode */
+       if (!is_protmode(vcpu))
                return 0;
 
        if (vmx_get_rflags(vcpu) & X86_EFLAGS_VM) /* if virtual 8086 */
@@ -2042,7 +2099,7 @@ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu)
 static bool guest_state_valid(struct kvm_vcpu *vcpu)
 {
        /* real mode guest state checks */
-       if (!(vcpu->arch.cr0 & X86_CR0_PE)) {
+       if (!is_protmode(vcpu)) {
                if (!rmode_segment_valid(vcpu, VCPU_SREG_CS))
                        return false;
                if (!rmode_segment_valid(vcpu, VCPU_SREG_SS))
@@ -2175,7 +2232,7 @@ static int alloc_apic_access_page(struct kvm *kvm)
        struct kvm_userspace_memory_region kvm_userspace_mem;
        int r = 0;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
        if (kvm->arch.apic_access_page)
                goto out;
        kvm_userspace_mem.slot = APIC_ACCESS_PAGE_PRIVATE_MEMSLOT;
@@ -2188,7 +2245,7 @@ static int alloc_apic_access_page(struct kvm *kvm)
 
        kvm->arch.apic_access_page = gfn_to_page(kvm, 0xfee00);
 out:
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return r;
 }
 
@@ -2197,7 +2254,7 @@ static int alloc_identity_pagetable(struct kvm *kvm)
        struct kvm_userspace_memory_region kvm_userspace_mem;
        int r = 0;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
        if (kvm->arch.ept_identity_pagetable)
                goto out;
        kvm_userspace_mem.slot = IDENTITY_PAGETABLE_PRIVATE_MEMSLOT;
@@ -2212,7 +2269,7 @@ static int alloc_identity_pagetable(struct kvm *kvm)
        kvm->arch.ept_identity_pagetable = gfn_to_page(kvm,
                        kvm->arch.ept_identity_map_addr >> PAGE_SHIFT);
 out:
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return r;
 }
 
@@ -2384,14 +2441,12 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
        for (i = 0; i < NR_VMX_MSR; ++i) {
                u32 index = vmx_msr_index[i];
                u32 data_low, data_high;
-               u64 data;
                int j = vmx->nmsrs;
 
                if (rdmsr_safe(index, &data_low, &data_high) < 0)
                        continue;
                if (wrmsr_safe(index, data_low, data_high) < 0)
                        continue;
-               data = data_low | ((u64)data_high << 32);
                vmx->guest_msrs[j].index = i;
                vmx->guest_msrs[j].data = 0;
                vmx->guest_msrs[j].mask = -1ull;
@@ -2404,7 +2459,10 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
        vmcs_write32(VM_ENTRY_CONTROLS, vmcs_config.vmentry_ctrl);
 
        vmcs_writel(CR0_GUEST_HOST_MASK, ~0UL);
-       vmcs_writel(CR4_GUEST_HOST_MASK, KVM_GUEST_CR4_MASK);
+       vmx->vcpu.arch.cr4_guest_owned_bits = KVM_CR4_GUEST_OWNED_BITS;
+       if (enable_ept)
+               vmx->vcpu.arch.cr4_guest_owned_bits |= X86_CR4_PGE;
+       vmcs_writel(CR4_GUEST_HOST_MASK, ~vmx->vcpu.arch.cr4_guest_owned_bits);
 
        tsc_base = vmx->vcpu.kvm->arch.vm_init_tsc;
        rdtscll(tsc_this);
@@ -2429,10 +2487,10 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
        u64 msr;
-       int ret;
+       int ret, idx;
 
        vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP));
-       down_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
        if (!init_rmode(vmx->vcpu.kvm)) {
                ret = -ENOMEM;
                goto out;
@@ -2526,7 +2584,7 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
                vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid);
 
        vmx->vcpu.arch.cr0 = X86_CR0_NW | X86_CR0_CD | X86_CR0_ET;
-       vmx_set_cr0(&vmx->vcpu, vmx->vcpu.arch.cr0); /* enter rmode */
+       vmx_set_cr0(&vmx->vcpu, kvm_read_cr0(vcpu)); /* enter rmode */
        vmx_set_cr4(&vmx->vcpu, 0);
        vmx_set_efer(&vmx->vcpu, 0);
        vmx_fpu_activate(&vmx->vcpu);
@@ -2540,7 +2598,7 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
        vmx->emulation_required = 0;
 
 out:
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
        return ret;
 }
 
@@ -2717,6 +2775,12 @@ static int handle_rmode_exception(struct kvm_vcpu *vcpu,
                kvm_queue_exception(vcpu, vec);
                return 1;
        case BP_VECTOR:
+               /*
+                * Update instruction length as we may reinject the exception
+                * from user space while in guest debugging mode.
+                */
+               to_vmx(vcpu)->vcpu.arch.event_exit_inst_len =
+                       vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
                if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
                        return 0;
                /* fall through */
@@ -2839,6 +2903,13 @@ static int handle_exception(struct kvm_vcpu *vcpu)
                kvm_run->debug.arch.dr7 = vmcs_readl(GUEST_DR7);
                /* fall through */
        case BP_VECTOR:
+               /*
+                * Update instruction length as we may reinject #BP from
+                * user space while in guest debugging mode. Reading it for
+                * #DB as well causes no harm, it is not used in that case.
+                */
+               vmx->vcpu.arch.event_exit_inst_len =
+                       vmcs_read32(VM_EXIT_INSTRUCTION_LEN);
                kvm_run->exit_reason = KVM_EXIT_DEBUG;
                kvm_run->debug.arch.pc = vmcs_readl(GUEST_CS_BASE) + rip;
                kvm_run->debug.arch.exception = ex_no;
@@ -2940,11 +3011,10 @@ static int handle_cr(struct kvm_vcpu *vcpu)
                };
                break;
        case 2: /* clts */
-               vmx_fpu_deactivate(vcpu);
-               vcpu->arch.cr0 &= ~X86_CR0_TS;
-               vmcs_writel(CR0_READ_SHADOW, vcpu->arch.cr0);
-               vmx_fpu_activate(vcpu);
+               vmx_set_cr0(vcpu, kvm_read_cr0_bits(vcpu, ~X86_CR0_TS));
+               trace_kvm_cr_write(0, kvm_read_cr0(vcpu));
                skip_emulated_instruction(vcpu);
+               vmx_fpu_activate(vcpu);
                return 1;
        case 1: /*mov from cr*/
                switch (cr) {
@@ -2962,7 +3032,9 @@ static int handle_cr(struct kvm_vcpu *vcpu)
                }
                break;
        case 3: /* lmsw */
-               kvm_lmsw(vcpu, (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f);
+               val = (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f;
+               trace_kvm_cr_write(0, (kvm_read_cr0(vcpu) & ~0xful) | val);
+               kvm_lmsw(vcpu, val);
 
                skip_emulated_instruction(vcpu);
                return 1;
@@ -2975,12 +3047,22 @@ static int handle_cr(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int check_dr_alias(struct kvm_vcpu *vcpu)
+{
+       if (kvm_read_cr4_bits(vcpu, X86_CR4_DE)) {
+               kvm_queue_exception(vcpu, UD_VECTOR);
+               return -1;
+       }
+       return 0;
+}
+
 static int handle_dr(struct kvm_vcpu *vcpu)
 {
        unsigned long exit_qualification;
        unsigned long val;
        int dr, reg;
 
+       /* Do not handle if the CPL > 0, will trigger GP on re-entry */
        if (!kvm_require_cpl(vcpu, 0))
                return 1;
        dr = vmcs_readl(GUEST_DR7);
@@ -3016,14 +3098,20 @@ static int handle_dr(struct kvm_vcpu *vcpu)
                case 0 ... 3:
                        val = vcpu->arch.db[dr];
                        break;
+               case 4:
+                       if (check_dr_alias(vcpu) < 0)
+                               return 1;
+                       /* fall through */
                case 6:
                        val = vcpu->arch.dr6;
                        break;
-               case 7:
+               case 5:
+                       if (check_dr_alias(vcpu) < 0)
+                               return 1;
+                       /* fall through */
+               default: /* 7 */
                        val = vcpu->arch.dr7;
                        break;
-               default:
-                       val = 0;
                }
                kvm_register_write(vcpu, reg, val);
        } else {
@@ -3034,21 +3122,25 @@ static int handle_dr(struct kvm_vcpu *vcpu)
                        if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
                                vcpu->arch.eff_db[dr] = val;
                        break;
-               case 4 ... 5:
-                       if (vcpu->arch.cr4 & X86_CR4_DE)
-                               kvm_queue_exception(vcpu, UD_VECTOR);
-                       break;
+               case 4:
+                       if (check_dr_alias(vcpu) < 0)
+                               return 1;
+                       /* fall through */
                case 6:
                        if (val & 0xffffffff00000000ULL) {
-                               kvm_queue_exception(vcpu, GP_VECTOR);
-                               break;
+                               kvm_inject_gp(vcpu, 0);
+                               return 1;
                        }
                        vcpu->arch.dr6 = (val & DR6_VOLATILE) | DR6_FIXED_1;
                        break;
-               case 7:
+               case 5:
+                       if (check_dr_alias(vcpu) < 0)
+                               return 1;
+                       /* fall through */
+               default: /* 7 */
                        if (val & 0xffffffff00000000ULL) {
-                               kvm_queue_exception(vcpu, GP_VECTOR);
-                               break;
+                               kvm_inject_gp(vcpu, 0);
+                               return 1;
                        }
                        vcpu->arch.dr7 = (val & DR7_VOLATILE) | DR7_FIXED_1;
                        if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) {
@@ -3075,6 +3167,7 @@ static int handle_rdmsr(struct kvm_vcpu *vcpu)
        u64 data;
 
        if (vmx_get_msr(vcpu, ecx, &data)) {
+               trace_kvm_msr_read_ex(ecx);
                kvm_inject_gp(vcpu, 0);
                return 1;
        }
@@ -3094,13 +3187,13 @@ static int handle_wrmsr(struct kvm_vcpu *vcpu)
        u64 data = (vcpu->arch.regs[VCPU_REGS_RAX] & -1u)
                | ((u64)(vcpu->arch.regs[VCPU_REGS_RDX] & -1u) << 32);
 
-       trace_kvm_msr_write(ecx, data);
-
        if (vmx_set_msr(vcpu, ecx, data) != 0) {
+               trace_kvm_msr_write_ex(ecx, data);
                kvm_inject_gp(vcpu, 0);
                return 1;
        }
 
+       trace_kvm_msr_write(ecx, data);
        skip_emulated_instruction(vcpu);
        return 1;
 }
@@ -3385,7 +3478,6 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
                }
 
                if (err != EMULATE_DONE) {
-                       kvm_report_emulation_failure(vcpu, "emulation failure");
                        vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
                        vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
                        vcpu->run->internal.ndata = 0;
@@ -3416,6 +3508,12 @@ static int handle_pause(struct kvm_vcpu *vcpu)
        return 1;
 }
 
+static int handle_invalid_op(struct kvm_vcpu *vcpu)
+{
+       kvm_queue_exception(vcpu, UD_VECTOR);
+       return 1;
+}
+
 /*
  * The exit handlers return 1 if the exit was handled fully and guest execution
  * may resume.  Otherwise they set the kvm_run parameter to indicate what needs
@@ -3453,6 +3551,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
        [EXIT_REASON_EPT_VIOLATION]           = handle_ept_violation,
        [EXIT_REASON_EPT_MISCONFIG]           = handle_ept_misconfig,
        [EXIT_REASON_PAUSE_INSTRUCTION]       = handle_pause,
+       [EXIT_REASON_MWAIT_INSTRUCTION]       = handle_invalid_op,
+       [EXIT_REASON_MONITOR_INSTRUCTION]     = handle_invalid_op,
 };
 
 static const int kvm_vmx_max_exit_handlers =
@@ -3686,9 +3786,6 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu)
         */
        vmcs_writel(HOST_CR0, read_cr0());
 
-       if (vcpu->arch.switch_db_regs)
-               set_debugreg(vcpu->arch.dr6, 6);
-
        asm(
                /* Store host registers */
                "push %%"R"dx; push %%"R"bp;"
@@ -3789,9 +3886,6 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu)
                                  | (1 << VCPU_EXREG_PDPTR));
        vcpu->arch.regs_dirty = 0;
 
-       if (vcpu->arch.switch_db_regs)
-               get_debugreg(vcpu->arch.dr6, 6);
-
        vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
        if (vmx->rmode.irq.pending)
                fixup_rmode_irq(vmx);
@@ -3920,7 +4014,7 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
         *   b. VT-d with snooping control feature: snooping control feature of
         *      VT-d engine can guarantee the cache correctness. Just set it
         *      to WB to keep consistent with host. So the same as item 3.
-        * 3. EPT without VT-d: always map as WB and set IGMT=1 to keep
+        * 3. EPT without VT-d: always map as WB and set IPAT=1 to keep
         *    consistent with host MTRR
         */
        if (is_mmio)
@@ -3931,37 +4025,88 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
                      VMX_EPT_MT_EPTE_SHIFT;
        else
                ret = (MTRR_TYPE_WRBACK << VMX_EPT_MT_EPTE_SHIFT)
-                       | VMX_EPT_IGMT_BIT;
+                       | VMX_EPT_IPAT_BIT;
 
        return ret;
 }
 
+#define _ER(x) { EXIT_REASON_##x, #x }
+
 static const struct trace_print_flags vmx_exit_reasons_str[] = {
-       { EXIT_REASON_EXCEPTION_NMI,           "exception" },
-       { EXIT_REASON_EXTERNAL_INTERRUPT,      "ext_irq" },
-       { EXIT_REASON_TRIPLE_FAULT,            "triple_fault" },
-       { EXIT_REASON_NMI_WINDOW,              "nmi_window" },
-       { EXIT_REASON_IO_INSTRUCTION,          "io_instruction" },
-       { EXIT_REASON_CR_ACCESS,               "cr_access" },
-       { EXIT_REASON_DR_ACCESS,               "dr_access" },
-       { EXIT_REASON_CPUID,                   "cpuid" },
-       { EXIT_REASON_MSR_READ,                "rdmsr" },
-       { EXIT_REASON_MSR_WRITE,               "wrmsr" },
-       { EXIT_REASON_PENDING_INTERRUPT,       "interrupt_window" },
-       { EXIT_REASON_HLT,                     "halt" },
-       { EXIT_REASON_INVLPG,                  "invlpg" },
-       { EXIT_REASON_VMCALL,                  "hypercall" },
-       { EXIT_REASON_TPR_BELOW_THRESHOLD,     "tpr_below_thres" },
-       { EXIT_REASON_APIC_ACCESS,             "apic_access" },
-       { EXIT_REASON_WBINVD,                  "wbinvd" },
-       { EXIT_REASON_TASK_SWITCH,             "task_switch" },
-       { EXIT_REASON_EPT_VIOLATION,           "ept_violation" },
+       _ER(EXCEPTION_NMI),
+       _ER(EXTERNAL_INTERRUPT),
+       _ER(TRIPLE_FAULT),
+       _ER(PENDING_INTERRUPT),
+       _ER(NMI_WINDOW),
+       _ER(TASK_SWITCH),
+       _ER(CPUID),
+       _ER(HLT),
+       _ER(INVLPG),
+       _ER(RDPMC),
+       _ER(RDTSC),
+       _ER(VMCALL),
+       _ER(VMCLEAR),
+       _ER(VMLAUNCH),
+       _ER(VMPTRLD),
+       _ER(VMPTRST),
+       _ER(VMREAD),
+       _ER(VMRESUME),
+       _ER(VMWRITE),
+       _ER(VMOFF),
+       _ER(VMON),
+       _ER(CR_ACCESS),
+       _ER(DR_ACCESS),
+       _ER(IO_INSTRUCTION),
+       _ER(MSR_READ),
+       _ER(MSR_WRITE),
+       _ER(MWAIT_INSTRUCTION),
+       _ER(MONITOR_INSTRUCTION),
+       _ER(PAUSE_INSTRUCTION),
+       _ER(MCE_DURING_VMENTRY),
+       _ER(TPR_BELOW_THRESHOLD),
+       _ER(APIC_ACCESS),
+       _ER(EPT_VIOLATION),
+       _ER(EPT_MISCONFIG),
+       _ER(WBINVD),
        { -1, NULL }
 };
 
-static bool vmx_gb_page_enable(void)
+#undef _ER
+
+static int vmx_get_lpage_level(void)
+{
+       if (enable_ept && !cpu_has_vmx_ept_1g_page())
+               return PT_DIRECTORY_LEVEL;
+       else
+               /* For shadow and EPT supported 1GB page */
+               return PT_PDPE_LEVEL;
+}
+
+static inline u32 bit(int bitno)
+{
+       return 1 << (bitno & 31);
+}
+
+static void vmx_cpuid_update(struct kvm_vcpu *vcpu)
 {
-       return false;
+       struct kvm_cpuid_entry2 *best;
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+       u32 exec_control;
+
+       vmx->rdtscp_enabled = false;
+       if (vmx_rdtscp_supported()) {
+               exec_control = vmcs_read32(SECONDARY_VM_EXEC_CONTROL);
+               if (exec_control & SECONDARY_EXEC_RDTSCP) {
+                       best = kvm_find_cpuid_entry(vcpu, 0x80000001, 0);
+                       if (best && (best->edx & bit(X86_FEATURE_RDTSCP)))
+                               vmx->rdtscp_enabled = true;
+                       else {
+                               exec_control &= ~SECONDARY_EXEC_RDTSCP;
+                               vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
+                                               exec_control);
+                       }
+               }
+       }
 }
 
 static struct kvm_x86_ops vmx_x86_ops = {
@@ -3990,6 +4135,7 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .set_segment = vmx_set_segment,
        .get_cpl = vmx_get_cpl,
        .get_cs_db_l_bits = vmx_get_cs_db_l_bits,
+       .decache_cr0_guest_bits = vmx_decache_cr0_guest_bits,
        .decache_cr4_guest_bits = vmx_decache_cr4_guest_bits,
        .set_cr0 = vmx_set_cr0,
        .set_cr3 = vmx_set_cr3,
@@ -4002,6 +4148,8 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .cache_reg = vmx_cache_reg,
        .get_rflags = vmx_get_rflags,
        .set_rflags = vmx_set_rflags,
+       .fpu_activate = vmx_fpu_activate,
+       .fpu_deactivate = vmx_fpu_deactivate,
 
        .tlb_flush = vmx_flush_tlb,
 
@@ -4027,7 +4175,11 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .get_mt_mask = vmx_get_mt_mask,
 
        .exit_reasons_str = vmx_exit_reasons_str,
-       .gb_page_enable = vmx_gb_page_enable,
+       .get_lpage_level = vmx_get_lpage_level,
+
+       .cpuid_update = vmx_cpuid_update,
+
+       .rdtscp_supported = vmx_rdtscp_supported,
 };
 
 static int __init vmx_init(void)
index a1e1bc9d412dadca87e4a7be6c7903ab0768a8a8..e46282a565658bdcc3ed2bae40677948cfcb3bf9 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/intel-iommu.h>
 #include <linux/cpufreq.h>
 #include <linux/user-return-notifier.h>
+#include <linux/srcu.h>
 #include <trace/events/kvm.h>
 #undef TRACE_INCLUDE_FILE
 #define CREATE_TRACE_POINTS
@@ -93,16 +94,16 @@ module_param_named(ignore_msrs, ignore_msrs, bool, S_IRUGO | S_IWUSR);
 
 struct kvm_shared_msrs_global {
        int nr;
-       struct kvm_shared_msr {
-               u32 msr;
-               u64 value;
-       } msrs[KVM_NR_SHARED_MSRS];
+       u32 msrs[KVM_NR_SHARED_MSRS];
 };
 
 struct kvm_shared_msrs {
        struct user_return_notifier urn;
        bool registered;
-       u64 current_value[KVM_NR_SHARED_MSRS];
+       struct kvm_shared_msr_values {
+               u64 host;
+               u64 curr;
+       } values[KVM_NR_SHARED_MSRS];
 };
 
 static struct kvm_shared_msrs_global __read_mostly shared_msrs_global;
@@ -147,53 +148,64 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
 static void kvm_on_user_return(struct user_return_notifier *urn)
 {
        unsigned slot;
-       struct kvm_shared_msr *global;
        struct kvm_shared_msrs *locals
                = container_of(urn, struct kvm_shared_msrs, urn);
+       struct kvm_shared_msr_values *values;
 
        for (slot = 0; slot < shared_msrs_global.nr; ++slot) {
-               global = &shared_msrs_global.msrs[slot];
-               if (global->value != locals->current_value[slot]) {
-                       wrmsrl(global->msr, global->value);
-                       locals->current_value[slot] = global->value;
+               values = &locals->values[slot];
+               if (values->host != values->curr) {
+                       wrmsrl(shared_msrs_global.msrs[slot], values->host);
+                       values->curr = values->host;
                }
        }
        locals->registered = false;
        user_return_notifier_unregister(urn);
 }
 
-void kvm_define_shared_msr(unsigned slot, u32 msr)
+static void shared_msr_update(unsigned slot, u32 msr)
 {
-       int cpu;
+       struct kvm_shared_msrs *smsr;
        u64 value;
 
+       smsr = &__get_cpu_var(shared_msrs);
+       /* only read, and nobody should modify it at this time,
+        * so don't need lock */
+       if (slot >= shared_msrs_global.nr) {
+               printk(KERN_ERR "kvm: invalid MSR slot!");
+               return;
+       }
+       rdmsrl_safe(msr, &value);
+       smsr->values[slot].host = value;
+       smsr->values[slot].curr = value;
+}
+
+void kvm_define_shared_msr(unsigned slot, u32 msr)
+{
        if (slot >= shared_msrs_global.nr)
                shared_msrs_global.nr = slot + 1;
-       shared_msrs_global.msrs[slot].msr = msr;
-       rdmsrl_safe(msr, &value);
-       shared_msrs_global.msrs[slot].value = value;
-       for_each_online_cpu(cpu)
-               per_cpu(shared_msrs, cpu).current_value[slot] = value;
+       shared_msrs_global.msrs[slot] = msr;
+       /* we need ensured the shared_msr_global have been updated */
+       smp_wmb();
 }
 EXPORT_SYMBOL_GPL(kvm_define_shared_msr);
 
 static void kvm_shared_msr_cpu_online(void)
 {
        unsigned i;
-       struct kvm_shared_msrs *locals = &__get_cpu_var(shared_msrs);
 
        for (i = 0; i < shared_msrs_global.nr; ++i)
-               locals->current_value[i] = shared_msrs_global.msrs[i].value;
+               shared_msr_update(i, shared_msrs_global.msrs[i]);
 }
 
 void kvm_set_shared_msr(unsigned slot, u64 value, u64 mask)
 {
        struct kvm_shared_msrs *smsr = &__get_cpu_var(shared_msrs);
 
-       if (((value ^ smsr->current_value[slot]) & mask) == 0)
+       if (((value ^ smsr->values[slot].curr) & mask) == 0)
                return;
-       smsr->current_value[slot] = value;
-       wrmsrl(shared_msrs_global.msrs[slot].msr, value);
+       smsr->values[slot].curr = value;
+       wrmsrl(shared_msrs_global.msrs[slot], value);
        if (!smsr->registered) {
                smsr->urn.on_user_return = kvm_on_user_return;
                user_return_notifier_register(&smsr->urn);
@@ -257,12 +269,68 @@ void kvm_set_apic_base(struct kvm_vcpu *vcpu, u64 data)
 }
 EXPORT_SYMBOL_GPL(kvm_set_apic_base);
 
+#define EXCPT_BENIGN           0
+#define EXCPT_CONTRIBUTORY     1
+#define EXCPT_PF               2
+
+static int exception_class(int vector)
+{
+       switch (vector) {
+       case PF_VECTOR:
+               return EXCPT_PF;
+       case DE_VECTOR:
+       case TS_VECTOR:
+       case NP_VECTOR:
+       case SS_VECTOR:
+       case GP_VECTOR:
+               return EXCPT_CONTRIBUTORY;
+       default:
+               break;
+       }
+       return EXCPT_BENIGN;
+}
+
+static void kvm_multiple_exception(struct kvm_vcpu *vcpu,
+               unsigned nr, bool has_error, u32 error_code)
+{
+       u32 prev_nr;
+       int class1, class2;
+
+       if (!vcpu->arch.exception.pending) {
+       queue:
+               vcpu->arch.exception.pending = true;
+               vcpu->arch.exception.has_error_code = has_error;
+               vcpu->arch.exception.nr = nr;
+               vcpu->arch.exception.error_code = error_code;
+               return;
+       }
+
+       /* to check exception */
+       prev_nr = vcpu->arch.exception.nr;
+       if (prev_nr == DF_VECTOR) {
+               /* triple fault -> shutdown */
+               set_bit(KVM_REQ_TRIPLE_FAULT, &vcpu->requests);
+               return;
+       }
+       class1 = exception_class(prev_nr);
+       class2 = exception_class(nr);
+       if ((class1 == EXCPT_CONTRIBUTORY && class2 == EXCPT_CONTRIBUTORY)
+               || (class1 == EXCPT_PF && class2 != EXCPT_BENIGN)) {
+               /* generate double fault per SDM Table 5-5 */
+               vcpu->arch.exception.pending = true;
+               vcpu->arch.exception.has_error_code = true;
+               vcpu->arch.exception.nr = DF_VECTOR;
+               vcpu->arch.exception.error_code = 0;
+       } else
+               /* replace previous exception with a new one in a hope
+                  that instruction re-execution will regenerate lost
+                  exception */
+               goto queue;
+}
+
 void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr)
 {
-       WARN_ON(vcpu->arch.exception.pending);
-       vcpu->arch.exception.pending = true;
-       vcpu->arch.exception.has_error_code = false;
-       vcpu->arch.exception.nr = nr;
+       kvm_multiple_exception(vcpu, nr, false, 0);
 }
 EXPORT_SYMBOL_GPL(kvm_queue_exception);
 
@@ -270,25 +338,6 @@ void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long addr,
                           u32 error_code)
 {
        ++vcpu->stat.pf_guest;
-
-       if (vcpu->arch.exception.pending) {
-               switch(vcpu->arch.exception.nr) {
-               case DF_VECTOR:
-                       /* triple fault -> shutdown */
-                       set_bit(KVM_REQ_TRIPLE_FAULT, &vcpu->requests);
-                       return;
-               case PF_VECTOR:
-                       vcpu->arch.exception.nr = DF_VECTOR;
-                       vcpu->arch.exception.error_code = 0;
-                       return;
-               default:
-                       /* replace previous exception with a new one in a hope
-                          that instruction re-execution will regenerate lost
-                          exception */
-                       vcpu->arch.exception.pending = false;
-                       break;
-               }
-       }
        vcpu->arch.cr2 = addr;
        kvm_queue_exception_e(vcpu, PF_VECTOR, error_code);
 }
@@ -301,11 +350,7 @@ EXPORT_SYMBOL_GPL(kvm_inject_nmi);
 
 void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code)
 {
-       WARN_ON(vcpu->arch.exception.pending);
-       vcpu->arch.exception.pending = true;
-       vcpu->arch.exception.has_error_code = true;
-       vcpu->arch.exception.nr = nr;
-       vcpu->arch.exception.error_code = error_code;
+       kvm_multiple_exception(vcpu, nr, true, error_code);
 }
 EXPORT_SYMBOL_GPL(kvm_queue_exception_e);
 
@@ -383,12 +428,18 @@ out:
 
 void kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 {
-       if (cr0 & CR0_RESERVED_BITS) {
+       cr0 |= X86_CR0_ET;
+
+#ifdef CONFIG_X86_64
+       if (cr0 & 0xffffffff00000000UL) {
                printk(KERN_DEBUG "set_cr0: 0x%lx #GP, reserved bits 0x%lx\n",
-                      cr0, vcpu->arch.cr0);
+                      cr0, kvm_read_cr0(vcpu));
                kvm_inject_gp(vcpu, 0);
                return;
        }
+#endif
+
+       cr0 &= ~CR0_RESERVED_BITS;
 
        if ((cr0 & X86_CR0_NW) && !(cr0 & X86_CR0_CD)) {
                printk(KERN_DEBUG "set_cr0: #GP, CD == 0 && NW == 1\n");
@@ -405,7 +456,7 @@ void kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 
        if (!is_paging(vcpu) && (cr0 & X86_CR0_PG)) {
 #ifdef CONFIG_X86_64
-               if ((vcpu->arch.shadow_efer & EFER_LME)) {
+               if ((vcpu->arch.efer & EFER_LME)) {
                        int cs_db, cs_l;
 
                        if (!is_pae(vcpu)) {
@@ -443,13 +494,13 @@ EXPORT_SYMBOL_GPL(kvm_set_cr0);
 
 void kvm_lmsw(struct kvm_vcpu *vcpu, unsigned long msw)
 {
-       kvm_set_cr0(vcpu, (vcpu->arch.cr0 & ~0x0ful) | (msw & 0x0f));
+       kvm_set_cr0(vcpu, kvm_read_cr0_bits(vcpu, ~0x0ful) | (msw & 0x0f));
 }
 EXPORT_SYMBOL_GPL(kvm_lmsw);
 
 void kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
 {
-       unsigned long old_cr4 = vcpu->arch.cr4;
+       unsigned long old_cr4 = kvm_read_cr4(vcpu);
        unsigned long pdptr_bits = X86_CR4_PGE | X86_CR4_PSE | X86_CR4_PAE;
 
        if (cr4 & CR4_RESERVED_BITS) {
@@ -575,9 +626,11 @@ static inline u32 bit(int bitno)
  * kvm-specific. Those are put in the beginning of the list.
  */
 
-#define KVM_SAVE_MSRS_BEGIN    2
+#define KVM_SAVE_MSRS_BEGIN    5
 static u32 msrs_to_save[] = {
        MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
+       HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL,
+       HV_X64_MSR_APIC_ASSIST_PAGE,
        MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
        MSR_K6_STAR,
 #ifdef CONFIG_X86_64
@@ -602,7 +655,7 @@ static void set_efer(struct kvm_vcpu *vcpu, u64 efer)
        }
 
        if (is_paging(vcpu)
-           && (vcpu->arch.shadow_efer & EFER_LME) != (efer & EFER_LME)) {
+           && (vcpu->arch.efer & EFER_LME) != (efer & EFER_LME)) {
                printk(KERN_DEBUG "set_efer: #GP, change LME while paging\n");
                kvm_inject_gp(vcpu, 0);
                return;
@@ -633,9 +686,9 @@ static void set_efer(struct kvm_vcpu *vcpu, u64 efer)
        kvm_x86_ops->set_efer(vcpu, efer);
 
        efer &= ~EFER_LMA;
-       efer |= vcpu->arch.shadow_efer & EFER_LMA;
+       efer |= vcpu->arch.efer & EFER_LMA;
 
-       vcpu->arch.shadow_efer = efer;
+       vcpu->arch.efer = efer;
 
        vcpu->arch.mmu.base_role.nxe = (efer & EFER_NX) && !tdp_enabled;
        kvm_mmu_reset_context(vcpu);
@@ -957,6 +1010,100 @@ out:
        return r;
 }
 
+static bool kvm_hv_hypercall_enabled(struct kvm *kvm)
+{
+       return kvm->arch.hv_hypercall & HV_X64_MSR_HYPERCALL_ENABLE;
+}
+
+static bool kvm_hv_msr_partition_wide(u32 msr)
+{
+       bool r = false;
+       switch (msr) {
+       case HV_X64_MSR_GUEST_OS_ID:
+       case HV_X64_MSR_HYPERCALL:
+               r = true;
+               break;
+       }
+
+       return r;
+}
+
+static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+       struct kvm *kvm = vcpu->kvm;
+
+       switch (msr) {
+       case HV_X64_MSR_GUEST_OS_ID:
+               kvm->arch.hv_guest_os_id = data;
+               /* setting guest os id to zero disables hypercall page */
+               if (!kvm->arch.hv_guest_os_id)
+                       kvm->arch.hv_hypercall &= ~HV_X64_MSR_HYPERCALL_ENABLE;
+               break;
+       case HV_X64_MSR_HYPERCALL: {
+               u64 gfn;
+               unsigned long addr;
+               u8 instructions[4];
+
+               /* if guest os id is not set hypercall should remain disabled */
+               if (!kvm->arch.hv_guest_os_id)
+                       break;
+               if (!(data & HV_X64_MSR_HYPERCALL_ENABLE)) {
+                       kvm->arch.hv_hypercall = data;
+                       break;
+               }
+               gfn = data >> HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT;
+               addr = gfn_to_hva(kvm, gfn);
+               if (kvm_is_error_hva(addr))
+                       return 1;
+               kvm_x86_ops->patch_hypercall(vcpu, instructions);
+               ((unsigned char *)instructions)[3] = 0xc3; /* ret */
+               if (copy_to_user((void __user *)addr, instructions, 4))
+                       return 1;
+               kvm->arch.hv_hypercall = data;
+               break;
+       }
+       default:
+               pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x "
+                         "data 0x%llx\n", msr, data);
+               return 1;
+       }
+       return 0;
+}
+
+static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+       switch (msr) {
+       case HV_X64_MSR_APIC_ASSIST_PAGE: {
+               unsigned long addr;
+
+               if (!(data & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE)) {
+                       vcpu->arch.hv_vapic = data;
+                       break;
+               }
+               addr = gfn_to_hva(vcpu->kvm, data >>
+                                 HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT);
+               if (kvm_is_error_hva(addr))
+                       return 1;
+               if (clear_user((void __user *)addr, PAGE_SIZE))
+                       return 1;
+               vcpu->arch.hv_vapic = data;
+               break;
+       }
+       case HV_X64_MSR_EOI:
+               return kvm_hv_vapic_msr_write(vcpu, APIC_EOI, data);
+       case HV_X64_MSR_ICR:
+               return kvm_hv_vapic_msr_write(vcpu, APIC_ICR, data);
+       case HV_X64_MSR_TPR:
+               return kvm_hv_vapic_msr_write(vcpu, APIC_TASKPRI, data);
+       default:
+               pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x "
+                         "data 0x%llx\n", msr, data);
+               return 1;
+       }
+
+       return 0;
+}
+
 int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
 {
        switch (msr) {
@@ -1071,6 +1218,16 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
                pr_unimpl(vcpu, "unimplemented perfctr wrmsr: "
                        "0x%x data 0x%llx\n", msr, data);
                break;
+       case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15:
+               if (kvm_hv_msr_partition_wide(msr)) {
+                       int r;
+                       mutex_lock(&vcpu->kvm->lock);
+                       r = set_msr_hyperv_pw(vcpu, msr, data);
+                       mutex_unlock(&vcpu->kvm->lock);
+                       return r;
+               } else
+                       return set_msr_hyperv(vcpu, msr, data);
+               break;
        default:
                if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
                        return xen_hvm_config(vcpu, data);
@@ -1170,6 +1327,54 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
        return 0;
 }
 
+static int get_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+{
+       u64 data = 0;
+       struct kvm *kvm = vcpu->kvm;
+
+       switch (msr) {
+       case HV_X64_MSR_GUEST_OS_ID:
+               data = kvm->arch.hv_guest_os_id;
+               break;
+       case HV_X64_MSR_HYPERCALL:
+               data = kvm->arch.hv_hypercall;
+               break;
+       default:
+               pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
+               return 1;
+       }
+
+       *pdata = data;
+       return 0;
+}
+
+static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+{
+       u64 data = 0;
+
+       switch (msr) {
+       case HV_X64_MSR_VP_INDEX: {
+               int r;
+               struct kvm_vcpu *v;
+               kvm_for_each_vcpu(r, v, vcpu->kvm)
+                       if (v == vcpu)
+                               data = r;
+               break;
+       }
+       case HV_X64_MSR_EOI:
+               return kvm_hv_vapic_msr_read(vcpu, APIC_EOI, pdata);
+       case HV_X64_MSR_ICR:
+               return kvm_hv_vapic_msr_read(vcpu, APIC_ICR, pdata);
+       case HV_X64_MSR_TPR:
+               return kvm_hv_vapic_msr_read(vcpu, APIC_TASKPRI, pdata);
+       default:
+               pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
+               return 1;
+       }
+       *pdata = data;
+       return 0;
+}
+
 int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
 {
        u64 data;
@@ -1221,7 +1426,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
                data |= (((uint64_t)4ULL) << 40);
                break;
        case MSR_EFER:
-               data = vcpu->arch.shadow_efer;
+               data = vcpu->arch.efer;
                break;
        case MSR_KVM_WALL_CLOCK:
                data = vcpu->kvm->arch.wall_clock;
@@ -1236,6 +1441,16 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
        case MSR_IA32_MCG_STATUS:
        case MSR_IA32_MC0_CTL ... MSR_IA32_MC0_CTL + 4 * KVM_MAX_MCE_BANKS - 1:
                return get_msr_mce(vcpu, msr, pdata);
+       case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15:
+               if (kvm_hv_msr_partition_wide(msr)) {
+                       int r;
+                       mutex_lock(&vcpu->kvm->lock);
+                       r = get_msr_hyperv_pw(vcpu, msr, pdata);
+                       mutex_unlock(&vcpu->kvm->lock);
+                       return r;
+               } else
+                       return get_msr_hyperv(vcpu, msr, pdata);
+               break;
        default:
                if (!ignore_msrs) {
                        pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr);
@@ -1261,15 +1476,15 @@ static int __msr_io(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs,
                    int (*do_msr)(struct kvm_vcpu *vcpu,
                                  unsigned index, u64 *data))
 {
-       int i;
+       int i, idx;
 
        vcpu_load(vcpu);
 
-       down_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
        for (i = 0; i < msrs->nmsrs; ++i)
                if (do_msr(vcpu, entries[i].index, &entries[i].data))
                        break;
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
 
        vcpu_put(vcpu);
 
@@ -1351,6 +1566,11 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_XEN_HVM:
        case KVM_CAP_ADJUST_CLOCK:
        case KVM_CAP_VCPU_EVENTS:
+       case KVM_CAP_HYPERV:
+       case KVM_CAP_HYPERV_VAPIC:
+       case KVM_CAP_HYPERV_SPIN:
+       case KVM_CAP_PCI_SEGMENT:
+       case KVM_CAP_X86_ROBUST_SINGLESTEP:
                r = 1;
                break;
        case KVM_CAP_COALESCED_MMIO:
@@ -1464,8 +1684,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
-       kvm_x86_ops->vcpu_put(vcpu);
        kvm_put_guest_fpu(vcpu);
+       kvm_x86_ops->vcpu_put(vcpu);
 }
 
 static int is_efer_nx(void)
@@ -1530,6 +1750,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
        cpuid_fix_nx_cap(vcpu);
        r = 0;
        kvm_apic_set_version(vcpu);
+       kvm_x86_ops->cpuid_update(vcpu);
 
 out_free:
        vfree(cpuid_entries);
@@ -1552,6 +1773,7 @@ static int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu,
                goto out;
        vcpu->arch.cpuid_nent = cpuid->nent;
        kvm_apic_set_version(vcpu);
+       kvm_x86_ops->cpuid_update(vcpu);
        return 0;
 
 out:
@@ -1594,12 +1816,15 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
                         u32 index, int *nent, int maxnent)
 {
        unsigned f_nx = is_efer_nx() ? F(NX) : 0;
-       unsigned f_gbpages = kvm_x86_ops->gb_page_enable() ? F(GBPAGES) : 0;
 #ifdef CONFIG_X86_64
+       unsigned f_gbpages = (kvm_x86_ops->get_lpage_level() == PT_PDPE_LEVEL)
+                               ? F(GBPAGES) : 0;
        unsigned f_lm = F(LM);
 #else
+       unsigned f_gbpages = 0;
        unsigned f_lm = 0;
 #endif
+       unsigned f_rdtscp = kvm_x86_ops->rdtscp_supported() ? F(RDTSCP) : 0;
 
        /* cpuid 1.edx */
        const u32 kvm_supported_word0_x86_features =
@@ -1619,7 +1844,7 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
                F(MTRR) | F(PGE) | F(MCA) | F(CMOV) |
                F(PAT) | F(PSE36) | 0 /* Reserved */ |
                f_nx | 0 /* Reserved */ | F(MMXEXT) | F(MMX) |
-               F(FXSR) | F(FXSR_OPT) | f_gbpages | 0 /* RDTSCP */ |
+               F(FXSR) | F(FXSR_OPT) | f_gbpages | f_rdtscp |
                0 /* Reserved */ | f_lm | F(3DNOWEXT) | F(3DNOW);
        /* cpuid 1.ecx */
        const u32 kvm_supported_word4_x86_features =
@@ -1866,7 +2091,7 @@ static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu,
                return 0;
        if (mce->status & MCI_STATUS_UC) {
                if ((vcpu->arch.mcg_status & MCG_STATUS_MCIP) ||
-                   !(vcpu->arch.cr4 & X86_CR4_MCE)) {
+                   !kvm_read_cr4_bits(vcpu, X86_CR4_MCE)) {
                        printk(KERN_DEBUG "kvm: set_mce: "
                               "injects mce exception while "
                               "previous one is in progress!\n");
@@ -2160,14 +2385,14 @@ static int kvm_vm_ioctl_set_nr_mmu_pages(struct kvm *kvm,
        if (kvm_nr_mmu_pages < KVM_MIN_ALLOC_MMU_PAGES)
                return -EINVAL;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
        spin_lock(&kvm->mmu_lock);
 
        kvm_mmu_change_mmu_pages(kvm, kvm_nr_mmu_pages);
        kvm->arch.n_requested_mmu_pages = kvm_nr_mmu_pages;
 
        spin_unlock(&kvm->mmu_lock);
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return 0;
 }
 
@@ -2176,13 +2401,35 @@ static int kvm_vm_ioctl_get_nr_mmu_pages(struct kvm *kvm)
        return kvm->arch.n_alloc_mmu_pages;
 }
 
+gfn_t unalias_gfn_instantiation(struct kvm *kvm, gfn_t gfn)
+{
+       int i;
+       struct kvm_mem_alias *alias;
+       struct kvm_mem_aliases *aliases;
+
+       aliases = rcu_dereference(kvm->arch.aliases);
+
+       for (i = 0; i < aliases->naliases; ++i) {
+               alias = &aliases->aliases[i];
+               if (alias->flags & KVM_ALIAS_INVALID)
+                       continue;
+               if (gfn >= alias->base_gfn
+                   && gfn < alias->base_gfn + alias->npages)
+                       return alias->target_gfn + gfn - alias->base_gfn;
+       }
+       return gfn;
+}
+
 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
 {
        int i;
        struct kvm_mem_alias *alias;
+       struct kvm_mem_aliases *aliases;
 
-       for (i = 0; i < kvm->arch.naliases; ++i) {
-               alias = &kvm->arch.aliases[i];
+       aliases = rcu_dereference(kvm->arch.aliases);
+
+       for (i = 0; i < aliases->naliases; ++i) {
+               alias = &aliases->aliases[i];
                if (gfn >= alias->base_gfn
                    && gfn < alias->base_gfn + alias->npages)
                        return alias->target_gfn + gfn - alias->base_gfn;
@@ -2200,6 +2447,7 @@ static int kvm_vm_ioctl_set_memory_alias(struct kvm *kvm,
 {
        int r, n;
        struct kvm_mem_alias *p;
+       struct kvm_mem_aliases *aliases, *old_aliases;
 
        r = -EINVAL;
        /* General sanity checks */
@@ -2216,26 +2464,48 @@ static int kvm_vm_ioctl_set_memory_alias(struct kvm *kvm,
            < alias->target_phys_addr)
                goto out;
 
-       down_write(&kvm->slots_lock);
-       spin_lock(&kvm->mmu_lock);
+       r = -ENOMEM;
+       aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
+       if (!aliases)
+               goto out;
+
+       mutex_lock(&kvm->slots_lock);
 
-       p = &kvm->arch.aliases[alias->slot];
+       /* invalidate any gfn reference in case of deletion/shrinking */
+       memcpy(aliases, kvm->arch.aliases, sizeof(struct kvm_mem_aliases));
+       aliases->aliases[alias->slot].flags |= KVM_ALIAS_INVALID;
+       old_aliases = kvm->arch.aliases;
+       rcu_assign_pointer(kvm->arch.aliases, aliases);
+       synchronize_srcu_expedited(&kvm->srcu);
+       kvm_mmu_zap_all(kvm);
+       kfree(old_aliases);
+
+       r = -ENOMEM;
+       aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
+       if (!aliases)
+               goto out_unlock;
+
+       memcpy(aliases, kvm->arch.aliases, sizeof(struct kvm_mem_aliases));
+
+       p = &aliases->aliases[alias->slot];
        p->base_gfn = alias->guest_phys_addr >> PAGE_SHIFT;
        p->npages = alias->memory_size >> PAGE_SHIFT;
        p->target_gfn = alias->target_phys_addr >> PAGE_SHIFT;
+       p->flags &= ~(KVM_ALIAS_INVALID);
 
        for (n = KVM_ALIAS_SLOTS; n > 0; --n)
-               if (kvm->arch.aliases[n - 1].npages)
+               if (aliases->aliases[n - 1].npages)
                        break;
-       kvm->arch.naliases = n;
+       aliases->naliases = n;
 
-       spin_unlock(&kvm->mmu_lock);
-       kvm_mmu_zap_all(kvm);
-
-       up_write(&kvm->slots_lock);
-
-       return 0;
+       old_aliases = kvm->arch.aliases;
+       rcu_assign_pointer(kvm->arch.aliases, aliases);
+       synchronize_srcu_expedited(&kvm->srcu);
+       kfree(old_aliases);
+       r = 0;
 
+out_unlock:
+       mutex_unlock(&kvm->slots_lock);
 out:
        return r;
 }
@@ -2273,18 +2543,18 @@ static int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip)
        r = 0;
        switch (chip->chip_id) {
        case KVM_IRQCHIP_PIC_MASTER:
-               spin_lock(&pic_irqchip(kvm)->lock);
+               raw_spin_lock(&pic_irqchip(kvm)->lock);
                memcpy(&pic_irqchip(kvm)->pics[0],
                        &chip->chip.pic,
                        sizeof(struct kvm_pic_state));
-               spin_unlock(&pic_irqchip(kvm)->lock);
+               raw_spin_unlock(&pic_irqchip(kvm)->lock);
                break;
        case KVM_IRQCHIP_PIC_SLAVE:
-               spin_lock(&pic_irqchip(kvm)->lock);
+               raw_spin_lock(&pic_irqchip(kvm)->lock);
                memcpy(&pic_irqchip(kvm)->pics[1],
                        &chip->chip.pic,
                        sizeof(struct kvm_pic_state));
-               spin_unlock(&pic_irqchip(kvm)->lock);
+               raw_spin_unlock(&pic_irqchip(kvm)->lock);
                break;
        case KVM_IRQCHIP_IOAPIC:
                r = kvm_set_ioapic(kvm, &chip->chip.ioapic);
@@ -2364,29 +2634,62 @@ static int kvm_vm_ioctl_reinject(struct kvm *kvm,
 int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
                                      struct kvm_dirty_log *log)
 {
-       int r;
-       int n;
+       int r, n, i;
        struct kvm_memory_slot *memslot;
-       int is_dirty = 0;
+       unsigned long is_dirty = 0;
+       unsigned long *dirty_bitmap = NULL;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
 
-       r = kvm_get_dirty_log(kvm, log, &is_dirty);
-       if (r)
+       r = -EINVAL;
+       if (log->slot >= KVM_MEMORY_SLOTS)
+               goto out;
+
+       memslot = &kvm->memslots->memslots[log->slot];
+       r = -ENOENT;
+       if (!memslot->dirty_bitmap)
+               goto out;
+
+       n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;
+
+       r = -ENOMEM;
+       dirty_bitmap = vmalloc(n);
+       if (!dirty_bitmap)
                goto out;
+       memset(dirty_bitmap, 0, n);
+
+       for (i = 0; !is_dirty && i < n/sizeof(long); i++)
+               is_dirty = memslot->dirty_bitmap[i];
 
        /* If nothing is dirty, don't bother messing with page tables. */
        if (is_dirty) {
+               struct kvm_memslots *slots, *old_slots;
+
                spin_lock(&kvm->mmu_lock);
                kvm_mmu_slot_remove_write_access(kvm, log->slot);
                spin_unlock(&kvm->mmu_lock);
-               memslot = &kvm->memslots[log->slot];
-               n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;
-               memset(memslot->dirty_bitmap, 0, n);
+
+               slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+               if (!slots)
+                       goto out_free;
+
+               memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
+               slots->memslots[log->slot].dirty_bitmap = dirty_bitmap;
+
+               old_slots = kvm->memslots;
+               rcu_assign_pointer(kvm->memslots, slots);
+               synchronize_srcu_expedited(&kvm->srcu);
+               dirty_bitmap = old_slots->memslots[log->slot].dirty_bitmap;
+               kfree(old_slots);
        }
+
        r = 0;
+       if (copy_to_user(log->dirty_bitmap, dirty_bitmap, n))
+               r = -EFAULT;
+out_free:
+       vfree(dirty_bitmap);
 out:
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return r;
 }
 
@@ -2469,6 +2772,8 @@ long kvm_arch_vm_ioctl(struct file *filp,
                if (vpic) {
                        r = kvm_ioapic_init(kvm);
                        if (r) {
+                               kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS,
+                                                         &vpic->dev);
                                kfree(vpic);
                                goto create_irqchip_unlock;
                        }
@@ -2480,10 +2785,8 @@ long kvm_arch_vm_ioctl(struct file *filp,
                r = kvm_setup_default_irq_routing(kvm);
                if (r) {
                        mutex_lock(&kvm->irq_lock);
-                       kfree(kvm->arch.vpic);
-                       kfree(kvm->arch.vioapic);
-                       kvm->arch.vpic = NULL;
-                       kvm->arch.vioapic = NULL;
+                       kvm_ioapic_destroy(kvm);
+                       kvm_destroy_pic(kvm);
                        mutex_unlock(&kvm->irq_lock);
                }
        create_irqchip_unlock:
@@ -2499,7 +2802,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
                                   sizeof(struct kvm_pit_config)))
                        goto out;
        create_pit:
-               down_write(&kvm->slots_lock);
+               mutex_lock(&kvm->slots_lock);
                r = -EEXIST;
                if (kvm->arch.vpit)
                        goto create_pit_unlock;
@@ -2508,7 +2811,7 @@ long kvm_arch_vm_ioctl(struct file *filp,
                if (kvm->arch.vpit)
                        r = 0;
        create_pit_unlock:
-               up_write(&kvm->slots_lock);
+               mutex_unlock(&kvm->slots_lock);
                break;
        case KVM_IRQ_LINE_STATUS:
        case KVM_IRQ_LINE: {
@@ -2725,7 +3028,7 @@ static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,
            !kvm_iodevice_write(&vcpu->arch.apic->dev, addr, len, v))
                return 0;
 
-       return kvm_io_bus_write(&vcpu->kvm->mmio_bus, addr, len, v);
+       return kvm_io_bus_write(vcpu->kvm, KVM_MMIO_BUS, addr, len, v);
 }
 
 static int vcpu_mmio_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *v)
@@ -2734,17 +3037,44 @@ static int vcpu_mmio_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *v)
            !kvm_iodevice_read(&vcpu->arch.apic->dev, addr, len, v))
                return 0;
 
-       return kvm_io_bus_read(&vcpu->kvm->mmio_bus, addr, len, v);
+       return kvm_io_bus_read(vcpu->kvm, KVM_MMIO_BUS, addr, len, v);
 }
 
-static int kvm_read_guest_virt(gva_t addr, void *val, unsigned int bytes,
-                              struct kvm_vcpu *vcpu)
+gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+{
+       u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
+       return vcpu->arch.mmu.gva_to_gpa(vcpu, gva, access, error);
+}
+
+ gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+{
+       u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
+       access |= PFERR_FETCH_MASK;
+       return vcpu->arch.mmu.gva_to_gpa(vcpu, gva, access, error);
+}
+
+gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+{
+       u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
+       access |= PFERR_WRITE_MASK;
+       return vcpu->arch.mmu.gva_to_gpa(vcpu, gva, access, error);
+}
+
+/* uses this to access any guest's mapped memory without checking CPL */
+gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, u32 *error)
+{
+       return vcpu->arch.mmu.gva_to_gpa(vcpu, gva, 0, error);
+}
+
+static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes,
+                                     struct kvm_vcpu *vcpu, u32 access,
+                                     u32 *error)
 {
        void *data = val;
        int r = X86EMUL_CONTINUE;
 
        while (bytes) {
-               gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr);
+               gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr, access, error);
                unsigned offset = addr & (PAGE_SIZE-1);
                unsigned toread = min(bytes, (unsigned)PAGE_SIZE - offset);
                int ret;
@@ -2767,14 +3097,37 @@ out:
        return r;
 }
 
+/* used for instruction fetching */
+static int kvm_fetch_guest_virt(gva_t addr, void *val, unsigned int bytes,
+                               struct kvm_vcpu *vcpu, u32 *error)
+{
+       u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
+       return kvm_read_guest_virt_helper(addr, val, bytes, vcpu,
+                                         access | PFERR_FETCH_MASK, error);
+}
+
+static int kvm_read_guest_virt(gva_t addr, void *val, unsigned int bytes,
+                              struct kvm_vcpu *vcpu, u32 *error)
+{
+       u32 access = (kvm_x86_ops->get_cpl(vcpu) == 3) ? PFERR_USER_MASK : 0;
+       return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, access,
+                                         error);
+}
+
+static int kvm_read_guest_virt_system(gva_t addr, void *val, unsigned int bytes,
+                              struct kvm_vcpu *vcpu, u32 *error)
+{
+       return kvm_read_guest_virt_helper(addr, val, bytes, vcpu, 0, error);
+}
+
 static int kvm_write_guest_virt(gva_t addr, void *val, unsigned int bytes,
-                               struct kvm_vcpu *vcpu)
+                               struct kvm_vcpu *vcpu, u32 *error)
 {
        void *data = val;
        int r = X86EMUL_CONTINUE;
 
        while (bytes) {
-               gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr);
+               gpa_t gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, error);
                unsigned offset = addr & (PAGE_SIZE-1);
                unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset);
                int ret;
@@ -2804,6 +3157,7 @@ static int emulator_read_emulated(unsigned long addr,
                                  struct kvm_vcpu *vcpu)
 {
        gpa_t                 gpa;
+       u32 error_code;
 
        if (vcpu->mmio_read_completed) {
                memcpy(val, vcpu->mmio_data, bytes);
@@ -2813,17 +3167,20 @@ static int emulator_read_emulated(unsigned long addr,
                return X86EMUL_CONTINUE;
        }
 
-       gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr);
+       gpa = kvm_mmu_gva_to_gpa_read(vcpu, addr, &error_code);
+
+       if (gpa == UNMAPPED_GVA) {
+               kvm_inject_page_fault(vcpu, addr, error_code);
+               return X86EMUL_PROPAGATE_FAULT;
+       }
 
        /* For APIC access vmexit */
        if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
                goto mmio;
 
-       if (kvm_read_guest_virt(addr, val, bytes, vcpu)
+       if (kvm_read_guest_virt(addr, val, bytes, vcpu, NULL)
                                == X86EMUL_CONTINUE)
                return X86EMUL_CONTINUE;
-       if (gpa == UNMAPPED_GVA)
-               return X86EMUL_PROPAGATE_FAULT;
 
 mmio:
        /*
@@ -2862,11 +3219,12 @@ static int emulator_write_emulated_onepage(unsigned long addr,
                                           struct kvm_vcpu *vcpu)
 {
        gpa_t                 gpa;
+       u32 error_code;
 
-       gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr);
+       gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, &error_code);
 
        if (gpa == UNMAPPED_GVA) {
-               kvm_inject_page_fault(vcpu, addr, 2);
+               kvm_inject_page_fault(vcpu, addr, error_code);
                return X86EMUL_PROPAGATE_FAULT;
        }
 
@@ -2930,7 +3288,7 @@ static int emulator_cmpxchg_emulated(unsigned long addr,
                char *kaddr;
                u64 val;
 
-               gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr);
+               gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, NULL);
 
                if (gpa == UNMAPPED_GVA ||
                   (gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
@@ -2967,35 +3325,21 @@ int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address)
 
 int emulate_clts(struct kvm_vcpu *vcpu)
 {
-       kvm_x86_ops->set_cr0(vcpu, vcpu->arch.cr0 & ~X86_CR0_TS);
+       kvm_x86_ops->set_cr0(vcpu, kvm_read_cr0_bits(vcpu, ~X86_CR0_TS));
+       kvm_x86_ops->fpu_activate(vcpu);
        return X86EMUL_CONTINUE;
 }
 
 int emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long *dest)
 {
-       struct kvm_vcpu *vcpu = ctxt->vcpu;
-
-       switch (dr) {
-       case 0 ... 3:
-               *dest = kvm_x86_ops->get_dr(vcpu, dr);
-               return X86EMUL_CONTINUE;
-       default:
-               pr_unimpl(vcpu, "%s: unexpected dr %u\n", __func__, dr);
-               return X86EMUL_UNHANDLEABLE;
-       }
+       return kvm_x86_ops->get_dr(ctxt->vcpu, dr, dest);
 }
 
 int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long value)
 {
        unsigned long mask = (ctxt->mode == X86EMUL_MODE_PROT64) ? ~0ULL : ~0U;
-       int exception;
 
-       kvm_x86_ops->set_dr(ctxt->vcpu, dr, value & mask, &exception);
-       if (exception) {
-               /* FIXME: better handling */
-               return X86EMUL_UNHANDLEABLE;
-       }
-       return X86EMUL_CONTINUE;
+       return kvm_x86_ops->set_dr(ctxt->vcpu, dr, value & mask);
 }
 
 void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context)
@@ -3009,7 +3353,7 @@ void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context)
 
        rip_linear = rip + get_segment_base(vcpu, VCPU_SREG_CS);
 
-       kvm_read_guest_virt(rip_linear, (void *)opcodes, 4, vcpu);
+       kvm_read_guest_virt(rip_linear, (void *)opcodes, 4, vcpu, NULL);
 
        printk(KERN_ERR "emulation failed (%s) rip %lx %02x %02x %02x %02x\n",
               context, rip, opcodes[0], opcodes[1], opcodes[2], opcodes[3]);
@@ -3017,7 +3361,8 @@ void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context)
 EXPORT_SYMBOL_GPL(kvm_report_emulation_failure);
 
 static struct x86_emulate_ops emulate_ops = {
-       .read_std            = kvm_read_guest_virt,
+       .read_std            = kvm_read_guest_virt_system,
+       .fetch               = kvm_fetch_guest_virt,
        .read_emulated       = emulator_read_emulated,
        .write_emulated      = emulator_write_emulated,
        .cmpxchg_emulated    = emulator_cmpxchg_emulated,
@@ -3060,8 +3405,9 @@ int emulate_instruction(struct kvm_vcpu *vcpu,
                vcpu->arch.emulate_ctxt.vcpu = vcpu;
                vcpu->arch.emulate_ctxt.eflags = kvm_get_rflags(vcpu);
                vcpu->arch.emulate_ctxt.mode =
+                       (!is_protmode(vcpu)) ? X86EMUL_MODE_REAL :
                        (vcpu->arch.emulate_ctxt.eflags & X86_EFLAGS_VM)
-                       ? X86EMUL_MODE_REAL : cs_l
+                       ? X86EMUL_MODE_VM86 : cs_l
                        ? X86EMUL_MODE_PROT64 : cs_db
                        ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16;
 
@@ -3153,12 +3499,17 @@ static int pio_copy_data(struct kvm_vcpu *vcpu)
        gva_t q = vcpu->arch.pio.guest_gva;
        unsigned bytes;
        int ret;
+       u32 error_code;
 
        bytes = vcpu->arch.pio.size * vcpu->arch.pio.cur_count;
        if (vcpu->arch.pio.in)
-               ret = kvm_write_guest_virt(q, p, bytes, vcpu);
+               ret = kvm_write_guest_virt(q, p, bytes, vcpu, &error_code);
        else
-               ret = kvm_read_guest_virt(q, p, bytes, vcpu);
+               ret = kvm_read_guest_virt(q, p, bytes, vcpu, &error_code);
+
+       if (ret == X86EMUL_PROPAGATE_FAULT)
+               kvm_inject_page_fault(vcpu, q, error_code);
+
        return ret;
 }
 
@@ -3179,7 +3530,7 @@ int complete_pio(struct kvm_vcpu *vcpu)
                if (io->in) {
                        r = pio_copy_data(vcpu);
                        if (r)
-                               return r;
+                               goto out;
                }
 
                delta = 1;
@@ -3206,7 +3557,7 @@ int complete_pio(struct kvm_vcpu *vcpu)
                        kvm_register_write(vcpu, VCPU_REGS_RSI, val);
                }
        }
-
+out:
        io->count -= io->cur_count;
        io->cur_count = 0;
 
@@ -3219,11 +3570,12 @@ static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
        int r;
 
        if (vcpu->arch.pio.in)
-               r = kvm_io_bus_read(&vcpu->kvm->pio_bus, vcpu->arch.pio.port,
+               r = kvm_io_bus_read(vcpu->kvm, KVM_PIO_BUS, vcpu->arch.pio.port,
                                    vcpu->arch.pio.size, pd);
        else
-               r = kvm_io_bus_write(&vcpu->kvm->pio_bus, vcpu->arch.pio.port,
-                                    vcpu->arch.pio.size, pd);
+               r = kvm_io_bus_write(vcpu->kvm, KVM_PIO_BUS,
+                                    vcpu->arch.pio.port, vcpu->arch.pio.size,
+                                    pd);
        return r;
 }
 
@@ -3234,7 +3586,7 @@ static int pio_string_write(struct kvm_vcpu *vcpu)
        int i, r = 0;
 
        for (i = 0; i < io->cur_count; i++) {
-               if (kvm_io_bus_write(&vcpu->kvm->pio_bus,
+               if (kvm_io_bus_write(vcpu->kvm, KVM_PIO_BUS,
                                     io->port, io->size, pd)) {
                        r = -EOPNOTSUPP;
                        break;
@@ -3248,6 +3600,8 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, int in, int size, unsigned port)
 {
        unsigned long val;
 
+       trace_kvm_pio(!in, port, size, 1);
+
        vcpu->run->exit_reason = KVM_EXIT_IO;
        vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT;
        vcpu->run->io.size = vcpu->arch.pio.size = size;
@@ -3259,11 +3613,10 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, int in, int size, unsigned port)
        vcpu->arch.pio.down = 0;
        vcpu->arch.pio.rep = 0;
 
-       trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port,
-                     size, 1);
-
-       val = kvm_register_read(vcpu, VCPU_REGS_RAX);
-       memcpy(vcpu->arch.pio_data, &val, 4);
+       if (!vcpu->arch.pio.in) {
+               val = kvm_register_read(vcpu, VCPU_REGS_RAX);
+               memcpy(vcpu->arch.pio_data, &val, 4);
+       }
 
        if (!kernel_pio(vcpu, vcpu->arch.pio_data)) {
                complete_pio(vcpu);
@@ -3280,6 +3633,8 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, int in,
        unsigned now, in_page;
        int ret = 0;
 
+       trace_kvm_pio(!in, port, size, count);
+
        vcpu->run->exit_reason = KVM_EXIT_IO;
        vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT;
        vcpu->run->io.size = vcpu->arch.pio.size = size;
@@ -3291,9 +3646,6 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, int in,
        vcpu->arch.pio.down = down;
        vcpu->arch.pio.rep = rep;
 
-       trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port,
-                     size, count);
-
        if (!count) {
                kvm_x86_ops->skip_emulated_instruction(vcpu);
                return 1;
@@ -3325,10 +3677,8 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, int in,
        if (!vcpu->arch.pio.in) {
                /* string PIO write */
                ret = pio_copy_data(vcpu);
-               if (ret == X86EMUL_PROPAGATE_FAULT) {
-                       kvm_inject_gp(vcpu, 0);
+               if (ret == X86EMUL_PROPAGATE_FAULT)
                        return 1;
-               }
                if (ret == 0 && !pio_string_write(vcpu)) {
                        complete_pio(vcpu);
                        if (vcpu->arch.pio.count == 0)
@@ -3487,11 +3837,76 @@ static inline gpa_t hc_gpa(struct kvm_vcpu *vcpu, unsigned long a0,
                return a0 | ((gpa_t)a1 << 32);
 }
 
+int kvm_hv_hypercall(struct kvm_vcpu *vcpu)
+{
+       u64 param, ingpa, outgpa, ret;
+       uint16_t code, rep_idx, rep_cnt, res = HV_STATUS_SUCCESS, rep_done = 0;
+       bool fast, longmode;
+       int cs_db, cs_l;
+
+       /*
+        * hypercall generates UD from non zero cpl and real mode
+        * per HYPER-V spec
+        */
+       if (kvm_x86_ops->get_cpl(vcpu) != 0 || !is_protmode(vcpu)) {
+               kvm_queue_exception(vcpu, UD_VECTOR);
+               return 0;
+       }
+
+       kvm_x86_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
+       longmode = is_long_mode(vcpu) && cs_l == 1;
+
+       if (!longmode) {
+               param = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDX) << 32) |
+                       (kvm_register_read(vcpu, VCPU_REGS_RAX) & 0xffffffff);
+               ingpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RBX) << 32) |
+                       (kvm_register_read(vcpu, VCPU_REGS_RCX) & 0xffffffff);
+               outgpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDI) << 32) |
+                       (kvm_register_read(vcpu, VCPU_REGS_RSI) & 0xffffffff);
+       }
+#ifdef CONFIG_X86_64
+       else {
+               param = kvm_register_read(vcpu, VCPU_REGS_RCX);
+               ingpa = kvm_register_read(vcpu, VCPU_REGS_RDX);
+               outgpa = kvm_register_read(vcpu, VCPU_REGS_R8);
+       }
+#endif
+
+       code = param & 0xffff;
+       fast = (param >> 16) & 0x1;
+       rep_cnt = (param >> 32) & 0xfff;
+       rep_idx = (param >> 48) & 0xfff;
+
+       trace_kvm_hv_hypercall(code, fast, rep_cnt, rep_idx, ingpa, outgpa);
+
+       switch (code) {
+       case HV_X64_HV_NOTIFY_LONG_SPIN_WAIT:
+               kvm_vcpu_on_spin(vcpu);
+               break;
+       default:
+               res = HV_STATUS_INVALID_HYPERCALL_CODE;
+               break;
+       }
+
+       ret = res | (((u64)rep_done & 0xfff) << 32);
+       if (longmode) {
+               kvm_register_write(vcpu, VCPU_REGS_RAX, ret);
+       } else {
+               kvm_register_write(vcpu, VCPU_REGS_RDX, ret >> 32);
+               kvm_register_write(vcpu, VCPU_REGS_RAX, ret & 0xffffffff);
+       }
+
+       return 1;
+}
+
 int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 {
        unsigned long nr, a0, a1, a2, a3, ret;
        int r = 1;
 
+       if (kvm_hv_hypercall_enabled(vcpu->kvm))
+               return kvm_hv_hypercall(vcpu);
+
        nr = kvm_register_read(vcpu, VCPU_REGS_RAX);
        a0 = kvm_register_read(vcpu, VCPU_REGS_RBX);
        a1 = kvm_register_read(vcpu, VCPU_REGS_RCX);
@@ -3534,10 +3949,8 @@ EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);
 int kvm_fix_hypercall(struct kvm_vcpu *vcpu)
 {
        char instruction[3];
-       int ret = 0;
        unsigned long rip = kvm_rip_read(vcpu);
 
-
        /*
         * Blow out the MMU to ensure that no other VCPU has an active mapping
         * to ensure that the updated hypercall appears atomically across all
@@ -3546,11 +3959,8 @@ int kvm_fix_hypercall(struct kvm_vcpu *vcpu)
        kvm_mmu_zap_all(vcpu->kvm);
 
        kvm_x86_ops->patch_hypercall(vcpu, instruction);
-       if (emulator_write_emulated(rip, instruction, 3, vcpu)
-           != X86EMUL_CONTINUE)
-               ret = -EFAULT;
 
-       return ret;
+       return emulator_write_emulated(rip, instruction, 3, vcpu);
 }
 
 static u64 mk_cr_64(u64 curr_cr, u32 new_val)
@@ -3583,10 +3993,9 @@ unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
 {
        unsigned long value;
 
-       kvm_x86_ops->decache_cr4_guest_bits(vcpu);
        switch (cr) {
        case 0:
-               value = vcpu->arch.cr0;
+               value = kvm_read_cr0(vcpu);
                break;
        case 2:
                value = vcpu->arch.cr2;
@@ -3595,7 +4004,7 @@ unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
                value = vcpu->arch.cr3;
                break;
        case 4:
-               value = vcpu->arch.cr4;
+               value = kvm_read_cr4(vcpu);
                break;
        case 8:
                value = kvm_get_cr8(vcpu);
@@ -3613,7 +4022,7 @@ void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val,
 {
        switch (cr) {
        case 0:
-               kvm_set_cr0(vcpu, mk_cr_64(vcpu->arch.cr0, val));
+               kvm_set_cr0(vcpu, mk_cr_64(kvm_read_cr0(vcpu), val));
                *rflags = kvm_get_rflags(vcpu);
                break;
        case 2:
@@ -3623,7 +4032,7 @@ void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val,
                kvm_set_cr3(vcpu, val);
                break;
        case 4:
-               kvm_set_cr4(vcpu, mk_cr_64(vcpu->arch.cr4, val));
+               kvm_set_cr4(vcpu, mk_cr_64(kvm_read_cr4(vcpu), val));
                break;
        case 8:
                kvm_set_cr8(vcpu, val & 0xfUL);
@@ -3690,6 +4099,7 @@ struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
        }
        return best;
 }
+EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry);
 
 int cpuid_maxphyaddr(struct kvm_vcpu *vcpu)
 {
@@ -3773,14 +4183,15 @@ static void vapic_enter(struct kvm_vcpu *vcpu)
 static void vapic_exit(struct kvm_vcpu *vcpu)
 {
        struct kvm_lapic *apic = vcpu->arch.apic;
+       int idx;
 
        if (!apic || !apic->vapic_addr)
                return;
 
-       down_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
        kvm_release_page_dirty(apic->vapic_page);
        mark_page_dirty(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT);
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
 }
 
 static void update_cr8_intercept(struct kvm_vcpu *vcpu)
@@ -3876,12 +4287,17 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                        r = 0;
                        goto out;
                }
+               if (test_and_clear_bit(KVM_REQ_DEACTIVATE_FPU, &vcpu->requests)) {
+                       vcpu->fpu_active = 0;
+                       kvm_x86_ops->fpu_deactivate(vcpu);
+               }
        }
 
        preempt_disable();
 
        kvm_x86_ops->prepare_guest_switch(vcpu);
-       kvm_load_guest_fpu(vcpu);
+       if (vcpu->fpu_active)
+               kvm_load_guest_fpu(vcpu);
 
        local_irq_disable();
 
@@ -3909,7 +4325,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                kvm_lapic_sync_to_vapic(vcpu);
        }
 
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
 
        kvm_guest_enter();
 
@@ -3951,7 +4367,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
 
        preempt_enable();
 
-       down_read(&vcpu->kvm->slots_lock);
+       vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
 
        /*
         * Profile KVM exit RIPs:
@@ -3973,6 +4389,7 @@ out:
 static int __vcpu_run(struct kvm_vcpu *vcpu)
 {
        int r;
+       struct kvm *kvm = vcpu->kvm;
 
        if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED)) {
                pr_debug("vcpu %d received sipi with vector # %x\n",
@@ -3984,7 +4401,7 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
        }
 
-       down_read(&vcpu->kvm->slots_lock);
+       vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
        vapic_enter(vcpu);
 
        r = 1;
@@ -3992,9 +4409,9 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE)
                        r = vcpu_enter_guest(vcpu);
                else {
-                       up_read(&vcpu->kvm->slots_lock);
+                       srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
                        kvm_vcpu_block(vcpu);
-                       down_read(&vcpu->kvm->slots_lock);
+                       vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
                        if (test_and_clear_bit(KVM_REQ_UNHALT, &vcpu->requests))
                        {
                                switch(vcpu->arch.mp_state) {
@@ -4029,13 +4446,13 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                        ++vcpu->stat.signal_exits;
                }
                if (need_resched()) {
-                       up_read(&vcpu->kvm->slots_lock);
+                       srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
                        kvm_resched(vcpu);
-                       down_read(&vcpu->kvm->slots_lock);
+                       vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
                }
        }
 
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
        post_kvm_run_save(vcpu);
 
        vapic_exit(vcpu);
@@ -4074,10 +4491,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                vcpu->mmio_read_completed = 1;
                vcpu->mmio_needed = 0;
 
-               down_read(&vcpu->kvm->slots_lock);
+               vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
                r = emulate_instruction(vcpu, vcpu->arch.mmio_fault_cr2, 0,
                                        EMULTYPE_NO_DECODE);
-               up_read(&vcpu->kvm->slots_lock);
+               srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
                if (r == EMULATE_DO_MMIO) {
                        /*
                         * Read-modify-write.  Back to userspace.
@@ -4204,13 +4621,12 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
        sregs->gdt.limit = dt.limit;
        sregs->gdt.base = dt.base;
 
-       kvm_x86_ops->decache_cr4_guest_bits(vcpu);
-       sregs->cr0 = vcpu->arch.cr0;
+       sregs->cr0 = kvm_read_cr0(vcpu);
        sregs->cr2 = vcpu->arch.cr2;
        sregs->cr3 = vcpu->arch.cr3;
-       sregs->cr4 = vcpu->arch.cr4;
+       sregs->cr4 = kvm_read_cr4(vcpu);
        sregs->cr8 = kvm_get_cr8(vcpu);
-       sregs->efer = vcpu->arch.shadow_efer;
+       sregs->efer = vcpu->arch.efer;
        sregs->apic_base = kvm_get_apic_base(vcpu);
 
        memset(sregs->interrupt_bitmap, 0, sizeof sregs->interrupt_bitmap);
@@ -4298,14 +4714,23 @@ static int load_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
 {
        struct descriptor_table dtable;
        u16 index = selector >> 3;
+       int ret;
+       u32 err;
+       gva_t addr;
 
        get_segment_descriptor_dtable(vcpu, selector, &dtable);
 
        if (dtable.limit < index * 8 + 7) {
                kvm_queue_exception_e(vcpu, GP_VECTOR, selector & 0xfffc);
-               return 1;
+               return X86EMUL_PROPAGATE_FAULT;
        }
-       return kvm_read_guest_virt(dtable.base + index*8, seg_desc, sizeof(*seg_desc), vcpu);
+       addr = dtable.base + index * 8;
+       ret = kvm_read_guest_virt_system(addr, seg_desc, sizeof(*seg_desc),
+                                        vcpu,  &err);
+       if (ret == X86EMUL_PROPAGATE_FAULT)
+               kvm_inject_page_fault(vcpu, addr, err);
+
+       return ret;
 }
 
 /* allowed just for 8 bytes segments */
@@ -4319,15 +4744,23 @@ static int save_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
 
        if (dtable.limit < index * 8 + 7)
                return 1;
-       return kvm_write_guest_virt(dtable.base + index*8, seg_desc, sizeof(*seg_desc), vcpu);
+       return kvm_write_guest_virt(dtable.base + index*8, seg_desc, sizeof(*seg_desc), vcpu, NULL);
+}
+
+static gpa_t get_tss_base_addr_write(struct kvm_vcpu *vcpu,
+                              struct desc_struct *seg_desc)
+{
+       u32 base_addr = get_desc_base(seg_desc);
+
+       return kvm_mmu_gva_to_gpa_write(vcpu, base_addr, NULL);
 }
 
-static gpa_t get_tss_base_addr(struct kvm_vcpu *vcpu,
+static gpa_t get_tss_base_addr_read(struct kvm_vcpu *vcpu,
                             struct desc_struct *seg_desc)
 {
        u32 base_addr = get_desc_base(seg_desc);
 
-       return vcpu->arch.mmu.gva_to_gpa(vcpu, base_addr);
+       return kvm_mmu_gva_to_gpa_read(vcpu, base_addr, NULL);
 }
 
 static u16 get_segment_selector(struct kvm_vcpu *vcpu, int seg)
@@ -4338,18 +4771,6 @@ static u16 get_segment_selector(struct kvm_vcpu *vcpu, int seg)
        return kvm_seg.selector;
 }
 
-static int load_segment_descriptor_to_kvm_desct(struct kvm_vcpu *vcpu,
-                                               u16 selector,
-                                               struct kvm_segment *kvm_seg)
-{
-       struct desc_struct seg_desc;
-
-       if (load_guest_segment_descriptor(vcpu, selector, &seg_desc))
-               return 1;
-       seg_desct_to_kvm_desct(&seg_desc, selector, kvm_seg);
-       return 0;
-}
-
 static int kvm_load_realmode_segment(struct kvm_vcpu *vcpu, u16 selector, int seg)
 {
        struct kvm_segment segvar = {
@@ -4367,7 +4788,7 @@ static int kvm_load_realmode_segment(struct kvm_vcpu *vcpu, u16 selector, int se
                .unusable = 0,
        };
        kvm_x86_ops->set_segment(vcpu, &segvar, seg);
-       return 0;
+       return X86EMUL_CONTINUE;
 }
 
 static int is_vm86_segment(struct kvm_vcpu *vcpu, int seg)
@@ -4377,24 +4798,112 @@ static int is_vm86_segment(struct kvm_vcpu *vcpu, int seg)
                (kvm_get_rflags(vcpu) & X86_EFLAGS_VM);
 }
 
-int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
-                               int type_bits, int seg)
+int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector, int seg)
 {
        struct kvm_segment kvm_seg;
+       struct desc_struct seg_desc;
+       u8 dpl, rpl, cpl;
+       unsigned err_vec = GP_VECTOR;
+       u32 err_code = 0;
+       bool null_selector = !(selector & ~0x3); /* 0000-0003 are null */
+       int ret;
 
-       if (is_vm86_segment(vcpu, seg) || !(vcpu->arch.cr0 & X86_CR0_PE))
+       if (is_vm86_segment(vcpu, seg) || !is_protmode(vcpu))
                return kvm_load_realmode_segment(vcpu, selector, seg);
-       if (load_segment_descriptor_to_kvm_desct(vcpu, selector, &kvm_seg))
-               return 1;
-       kvm_seg.type |= type_bits;
 
-       if (seg != VCPU_SREG_SS && seg != VCPU_SREG_CS &&
-           seg != VCPU_SREG_LDTR)
-               if (!kvm_seg.s)
-                       kvm_seg.unusable = 1;
+       /* NULL selector is not valid for TR, CS and SS */
+       if ((seg == VCPU_SREG_CS || seg == VCPU_SREG_SS || seg == VCPU_SREG_TR)
+           && null_selector)
+               goto exception;
+
+       /* TR should be in GDT only */
+       if (seg == VCPU_SREG_TR && (selector & (1 << 2)))
+               goto exception;
+
+       ret = load_guest_segment_descriptor(vcpu, selector, &seg_desc);
+       if (ret)
+               return ret;
+
+       seg_desct_to_kvm_desct(&seg_desc, selector, &kvm_seg);
+
+       if (null_selector) { /* for NULL selector skip all following checks */
+               kvm_seg.unusable = 1;
+               goto load;
+       }
+
+       err_code = selector & 0xfffc;
+       err_vec = GP_VECTOR;
 
+       /* can't load system descriptor into segment selecor */
+       if (seg <= VCPU_SREG_GS && !kvm_seg.s)
+               goto exception;
+
+       if (!kvm_seg.present) {
+               err_vec = (seg == VCPU_SREG_SS) ? SS_VECTOR : NP_VECTOR;
+               goto exception;
+       }
+
+       rpl = selector & 3;
+       dpl = kvm_seg.dpl;
+       cpl = kvm_x86_ops->get_cpl(vcpu);
+
+       switch (seg) {
+       case VCPU_SREG_SS:
+               /*
+                * segment is not a writable data segment or segment
+                * selector's RPL != CPL or segment selector's RPL != CPL
+                */
+               if (rpl != cpl || (kvm_seg.type & 0xa) != 0x2 || dpl != cpl)
+                       goto exception;
+               break;
+       case VCPU_SREG_CS:
+               if (!(kvm_seg.type & 8))
+                       goto exception;
+
+               if (kvm_seg.type & 4) {
+                       /* conforming */
+                       if (dpl > cpl)
+                               goto exception;
+               } else {
+                       /* nonconforming */
+                       if (rpl > cpl || dpl != cpl)
+                               goto exception;
+               }
+               /* CS(RPL) <- CPL */
+               selector = (selector & 0xfffc) | cpl;
+            break;
+       case VCPU_SREG_TR:
+               if (kvm_seg.s || (kvm_seg.type != 1 && kvm_seg.type != 9))
+                       goto exception;
+               break;
+       case VCPU_SREG_LDTR:
+               if (kvm_seg.s || kvm_seg.type != 2)
+                       goto exception;
+               break;
+       default: /*  DS, ES, FS, or GS */
+               /*
+                * segment is not a data or readable code segment or
+                * ((segment is a data or nonconforming code segment)
+                * and (both RPL and CPL > DPL))
+                */
+               if ((kvm_seg.type & 0xa) == 0x8 ||
+                   (((kvm_seg.type & 0xc) != 0xc) && (rpl > dpl && cpl > dpl)))
+                       goto exception;
+               break;
+       }
+
+       if (!kvm_seg.unusable && kvm_seg.s) {
+               /* mark segment as accessed */
+               kvm_seg.type |= 1;
+               seg_desc.type |= 1;
+               save_guest_segment_descriptor(vcpu, selector, &seg_desc);
+       }
+load:
        kvm_set_segment(vcpu, &kvm_seg, seg);
-       return 0;
+       return X86EMUL_CONTINUE;
+exception:
+       kvm_queue_exception_e(vcpu, err_vec, err_code);
+       return X86EMUL_PROPAGATE_FAULT;
 }
 
 static void save_state_to_tss32(struct kvm_vcpu *vcpu,
@@ -4420,6 +4929,14 @@ static void save_state_to_tss32(struct kvm_vcpu *vcpu,
        tss->ldt_selector = get_segment_selector(vcpu, VCPU_SREG_LDTR);
 }
 
+static void kvm_load_segment_selector(struct kvm_vcpu *vcpu, u16 sel, int seg)
+{
+       struct kvm_segment kvm_seg;
+       kvm_get_segment(vcpu, &kvm_seg, seg);
+       kvm_seg.selector = sel;
+       kvm_set_segment(vcpu, &kvm_seg, seg);
+}
+
 static int load_state_from_tss32(struct kvm_vcpu *vcpu,
                                  struct tss_segment_32 *tss)
 {
@@ -4437,25 +4954,41 @@ static int load_state_from_tss32(struct kvm_vcpu *vcpu,
        kvm_register_write(vcpu, VCPU_REGS_RSI, tss->esi);
        kvm_register_write(vcpu, VCPU_REGS_RDI, tss->edi);
 
-       if (kvm_load_segment_descriptor(vcpu, tss->ldt_selector, 0, VCPU_SREG_LDTR))
+       /*
+        * SDM says that segment selectors are loaded before segment
+        * descriptors
+        */
+       kvm_load_segment_selector(vcpu, tss->ldt_selector, VCPU_SREG_LDTR);
+       kvm_load_segment_selector(vcpu, tss->es, VCPU_SREG_ES);
+       kvm_load_segment_selector(vcpu, tss->cs, VCPU_SREG_CS);
+       kvm_load_segment_selector(vcpu, tss->ss, VCPU_SREG_SS);
+       kvm_load_segment_selector(vcpu, tss->ds, VCPU_SREG_DS);
+       kvm_load_segment_selector(vcpu, tss->fs, VCPU_SREG_FS);
+       kvm_load_segment_selector(vcpu, tss->gs, VCPU_SREG_GS);
+
+       /*
+        * Now load segment descriptors. If fault happenes at this stage
+        * it is handled in a context of new task
+        */
+       if (kvm_load_segment_descriptor(vcpu, tss->ldt_selector, VCPU_SREG_LDTR))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
+       if (kvm_load_segment_descriptor(vcpu, tss->es, VCPU_SREG_ES))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
+       if (kvm_load_segment_descriptor(vcpu, tss->cs, VCPU_SREG_CS))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
+       if (kvm_load_segment_descriptor(vcpu, tss->ss, VCPU_SREG_SS))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
+       if (kvm_load_segment_descriptor(vcpu, tss->ds, VCPU_SREG_DS))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->fs, 1, VCPU_SREG_FS))
+       if (kvm_load_segment_descriptor(vcpu, tss->fs, VCPU_SREG_FS))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->gs, 1, VCPU_SREG_GS))
+       if (kvm_load_segment_descriptor(vcpu, tss->gs, VCPU_SREG_GS))
                return 1;
        return 0;
 }
@@ -4495,19 +5028,33 @@ static int load_state_from_tss16(struct kvm_vcpu *vcpu,
        kvm_register_write(vcpu, VCPU_REGS_RSI, tss->si);
        kvm_register_write(vcpu, VCPU_REGS_RDI, tss->di);
 
-       if (kvm_load_segment_descriptor(vcpu, tss->ldt, 0, VCPU_SREG_LDTR))
+       /*
+        * SDM says that segment selectors are loaded before segment
+        * descriptors
+        */
+       kvm_load_segment_selector(vcpu, tss->ldt, VCPU_SREG_LDTR);
+       kvm_load_segment_selector(vcpu, tss->es, VCPU_SREG_ES);
+       kvm_load_segment_selector(vcpu, tss->cs, VCPU_SREG_CS);
+       kvm_load_segment_selector(vcpu, tss->ss, VCPU_SREG_SS);
+       kvm_load_segment_selector(vcpu, tss->ds, VCPU_SREG_DS);
+
+       /*
+        * Now load segment descriptors. If fault happenes at this stage
+        * it is handled in a context of new task
+        */
+       if (kvm_load_segment_descriptor(vcpu, tss->ldt, VCPU_SREG_LDTR))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
+       if (kvm_load_segment_descriptor(vcpu, tss->es, VCPU_SREG_ES))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
+       if (kvm_load_segment_descriptor(vcpu, tss->cs, VCPU_SREG_CS))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
+       if (kvm_load_segment_descriptor(vcpu, tss->ss, VCPU_SREG_SS))
                return 1;
 
-       if (kvm_load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
+       if (kvm_load_segment_descriptor(vcpu, tss->ds, VCPU_SREG_DS))
                return 1;
        return 0;
 }
@@ -4529,7 +5076,7 @@ static int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
                            sizeof tss_segment_16))
                goto out;
 
-       if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
+       if (kvm_read_guest(vcpu->kvm, get_tss_base_addr_read(vcpu, nseg_desc),
                           &tss_segment_16, sizeof tss_segment_16))
                goto out;
 
@@ -4537,7 +5084,7 @@ static int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
                tss_segment_16.prev_task_link = old_tss_sel;
 
                if (kvm_write_guest(vcpu->kvm,
-                                   get_tss_base_addr(vcpu, nseg_desc),
+                                   get_tss_base_addr_write(vcpu, nseg_desc),
                                    &tss_segment_16.prev_task_link,
                                    sizeof tss_segment_16.prev_task_link))
                        goto out;
@@ -4568,7 +5115,7 @@ static int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
                            sizeof tss_segment_32))
                goto out;
 
-       if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
+       if (kvm_read_guest(vcpu->kvm, get_tss_base_addr_read(vcpu, nseg_desc),
                           &tss_segment_32, sizeof tss_segment_32))
                goto out;
 
@@ -4576,7 +5123,7 @@ static int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
                tss_segment_32.prev_task_link = old_tss_sel;
 
                if (kvm_write_guest(vcpu->kvm,
-                                   get_tss_base_addr(vcpu, nseg_desc),
+                                   get_tss_base_addr_write(vcpu, nseg_desc),
                                    &tss_segment_32.prev_task_link,
                                    sizeof tss_segment_32.prev_task_link))
                        goto out;
@@ -4599,7 +5146,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
        u32 old_tss_base = get_segment_base(vcpu, VCPU_SREG_TR);
        u16 old_tss_sel = get_segment_selector(vcpu, VCPU_SREG_TR);
 
-       old_tss_base = vcpu->arch.mmu.gva_to_gpa(vcpu, old_tss_base);
+       old_tss_base = kvm_mmu_gva_to_gpa_write(vcpu, old_tss_base, NULL);
 
        /* FIXME: Handle errors. Failure to read either TSS or their
         * descriptors should generate a pagefault.
@@ -4658,7 +5205,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
                                              &nseg_desc);
        }
 
-       kvm_x86_ops->set_cr0(vcpu, vcpu->arch.cr0 | X86_CR0_TS);
+       kvm_x86_ops->set_cr0(vcpu, kvm_read_cr0(vcpu) | X86_CR0_TS);
        seg_desct_to_kvm_desct(&nseg_desc, tss_selector, &tr_seg);
        tr_seg.type = 11;
        kvm_set_segment(vcpu, &tr_seg, VCPU_SREG_TR);
@@ -4689,17 +5236,15 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
 
        kvm_set_cr8(vcpu, sregs->cr8);
 
-       mmu_reset_needed |= vcpu->arch.shadow_efer != sregs->efer;
+       mmu_reset_needed |= vcpu->arch.efer != sregs->efer;
        kvm_x86_ops->set_efer(vcpu, sregs->efer);
        kvm_set_apic_base(vcpu, sregs->apic_base);
 
-       kvm_x86_ops->decache_cr4_guest_bits(vcpu);
-
-       mmu_reset_needed |= vcpu->arch.cr0 != sregs->cr0;
+       mmu_reset_needed |= kvm_read_cr0(vcpu) != sregs->cr0;
        kvm_x86_ops->set_cr0(vcpu, sregs->cr0);
        vcpu->arch.cr0 = sregs->cr0;
 
-       mmu_reset_needed |= vcpu->arch.cr4 != sregs->cr4;
+       mmu_reset_needed |= kvm_read_cr4(vcpu) != sregs->cr4;
        kvm_x86_ops->set_cr4(vcpu, sregs->cr4);
        if (!is_long_mode(vcpu) && is_pae(vcpu)) {
                load_pdptrs(vcpu, vcpu->arch.cr3);
@@ -4734,7 +5279,7 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
        /* Older userspace won't unhalt the vcpu on reset. */
        if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 &&
            sregs->cs.selector == 0xf000 && sregs->cs.base == 0xffff0000 &&
-           !(vcpu->arch.cr0 & X86_CR0_PE))
+           !is_protmode(vcpu))
                vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
 
        vcpu_put(vcpu);
@@ -4832,11 +5377,12 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
 {
        unsigned long vaddr = tr->linear_address;
        gpa_t gpa;
+       int idx;
 
        vcpu_load(vcpu);
-       down_read(&vcpu->kvm->slots_lock);
-       gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, vaddr);
-       up_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
+       gpa = kvm_mmu_gva_to_gpa_system(vcpu, vaddr, NULL);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
        tr->physical_address = gpa;
        tr->valid = gpa != UNMAPPED_GVA;
        tr->writeable = 1;
@@ -4917,14 +5463,14 @@ EXPORT_SYMBOL_GPL(fx_init);
 
 void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
 {
-       if (!vcpu->fpu_active || vcpu->guest_fpu_loaded)
+       if (vcpu->guest_fpu_loaded)
                return;
 
        vcpu->guest_fpu_loaded = 1;
        kvm_fx_save(&vcpu->arch.host_fx_image);
        kvm_fx_restore(&vcpu->arch.guest_fx_image);
+       trace_kvm_fpu(1);
 }
-EXPORT_SYMBOL_GPL(kvm_load_guest_fpu);
 
 void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
 {
@@ -4935,8 +5481,9 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
        kvm_fx_save(&vcpu->arch.guest_fx_image);
        kvm_fx_restore(&vcpu->arch.host_fx_image);
        ++vcpu->stat.fpu_reload;
+       set_bit(KVM_REQ_DEACTIVATE_FPU, &vcpu->requests);
+       trace_kvm_fpu(0);
 }
-EXPORT_SYMBOL_GPL(kvm_put_guest_fpu);
 
 void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
 {
@@ -5088,11 +5635,13 @@ fail:
 
 void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
 {
+       int idx;
+
        kfree(vcpu->arch.mce_banks);
        kvm_free_lapic(vcpu);
-       down_read(&vcpu->kvm->slots_lock);
+       idx = srcu_read_lock(&vcpu->kvm->srcu);
        kvm_mmu_destroy(vcpu);
-       up_read(&vcpu->kvm->slots_lock);
+       srcu_read_unlock(&vcpu->kvm->srcu, idx);
        free_page((unsigned long)vcpu->arch.pio_data);
 }
 
@@ -5103,6 +5652,12 @@ struct  kvm *kvm_arch_create_vm(void)
        if (!kvm)
                return ERR_PTR(-ENOMEM);
 
+       kvm->arch.aliases = kzalloc(sizeof(struct kvm_mem_aliases), GFP_KERNEL);
+       if (!kvm->arch.aliases) {
+               kfree(kvm);
+               return ERR_PTR(-ENOMEM);
+       }
+
        INIT_LIST_HEAD(&kvm->arch.active_mmu_pages);
        INIT_LIST_HEAD(&kvm->arch.assigned_dev_head);
 
@@ -5159,16 +5714,18 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
                put_page(kvm->arch.apic_access_page);
        if (kvm->arch.ept_identity_pagetable)
                put_page(kvm->arch.ept_identity_pagetable);
+       cleanup_srcu_struct(&kvm->srcu);
+       kfree(kvm->arch.aliases);
        kfree(kvm);
 }
 
-int kvm_arch_set_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                               struct kvm_memory_slot *memslot,
                                struct kvm_memory_slot old,
+                               struct kvm_userspace_memory_region *mem,
                                int user_alloc)
 {
-       int npages = mem->memory_size >> PAGE_SHIFT;
-       struct kvm_memory_slot *memslot = &kvm->memslots[mem->slot];
+       int npages = memslot->npages;
 
        /*To keep backward compatibility with older userspace,
         *x86 needs to hanlde !user_alloc case.
@@ -5188,26 +5745,35 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
                        if (IS_ERR((void *)userspace_addr))
                                return PTR_ERR((void *)userspace_addr);
 
-                       /* set userspace_addr atomically for kvm_hva_to_rmapp */
-                       spin_lock(&kvm->mmu_lock);
                        memslot->userspace_addr = userspace_addr;
-                       spin_unlock(&kvm->mmu_lock);
-               } else {
-                       if (!old.user_alloc && old.rmap) {
-                               int ret;
-
-                               down_write(&current->mm->mmap_sem);
-                               ret = do_munmap(current->mm, old.userspace_addr,
-                                               old.npages * PAGE_SIZE);
-                               up_write(&current->mm->mmap_sem);
-                               if (ret < 0)
-                                       printk(KERN_WARNING
-                                      "kvm_vm_ioctl_set_memory_region: "
-                                      "failed to munmap memory\n");
-                       }
                }
        }
 
+
+       return 0;
+}
+
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+                               struct kvm_userspace_memory_region *mem,
+                               struct kvm_memory_slot old,
+                               int user_alloc)
+{
+
+       int npages = mem->memory_size >> PAGE_SHIFT;
+
+       if (!user_alloc && !old.user_alloc && old.rmap && !npages) {
+               int ret;
+
+               down_write(&current->mm->mmap_sem);
+               ret = do_munmap(current->mm, old.userspace_addr,
+                               old.npages * PAGE_SIZE);
+               up_write(&current->mm->mmap_sem);
+               if (ret < 0)
+                       printk(KERN_WARNING
+                              "kvm_vm_ioctl_set_memory_region: "
+                              "failed to munmap memory\n");
+       }
+
        spin_lock(&kvm->mmu_lock);
        if (!kvm->arch.n_requested_mmu_pages) {
                unsigned int nr_mmu_pages = kvm_mmu_calculate_mmu_pages(kvm);
@@ -5216,8 +5782,6 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
 
        kvm_mmu_slot_remove_write_access(kvm, mem->slot);
        spin_unlock(&kvm->mmu_lock);
-
-       return 0;
 }
 
 void kvm_arch_flush_shadow(struct kvm *kvm)
index 5eadea585d2a2084ef3adc91e7575e54b27aff21..2d101639bd8d776bf6f97252a3120a0cfa7730d7 100644 (file)
@@ -2,6 +2,7 @@
 #define ARCH_X86_KVM_X86_H
 
 #include <linux/kvm_host.h>
+#include "kvm_cache_regs.h"
 
 static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu)
 {
@@ -35,4 +36,33 @@ static inline bool kvm_exception_is_soft(unsigned int nr)
 struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
                                              u32 function, u32 index);
 
+static inline bool is_protmode(struct kvm_vcpu *vcpu)
+{
+       return kvm_read_cr0_bits(vcpu, X86_CR0_PE);
+}
+
+static inline int is_long_mode(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_X86_64
+       return vcpu->arch.efer & EFER_LMA;
+#else
+       return 0;
+#endif
+}
+
+static inline int is_pae(struct kvm_vcpu *vcpu)
+{
+       return kvm_read_cr4_bits(vcpu, X86_CR4_PAE);
+}
+
+static inline int is_pse(struct kvm_vcpu *vcpu)
+{
+       return kvm_read_cr4_bits(vcpu, X86_CR4_PSE);
+}
+
+static inline int is_paging(struct kvm_vcpu *vcpu)
+{
+       return kvm_read_cr0_bits(vcpu, X86_CR0_PG);
+}
+
 #endif
index 33a4ff45f8425eb84c5a0308fcf80c50ddc40d9a..b8c59b889c6ea579e7a6bacd7241bbbc86cbe44c 100644 (file)
@@ -78,7 +78,6 @@ int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err)
        walk->data -= walk->offset;
 
        if (nbytes && walk->offset & alignmask && !err) {
-               walk->offset += alignmask - 1;
                walk->offset = ALIGN(walk->offset, alignmask + 1);
                walk->data += walk->offset;
 
index 18870906ea065a9953e68171e3765c6e4bd4002b..2bb7348d8d5543364b1c75440e9d1c13ad1a0e2c 100644 (file)
@@ -386,11 +386,13 @@ static int crypto_authenc_encrypt(struct aead_request *req)
 {
        struct crypto_aead *authenc = crypto_aead_reqtfm(req);
        struct crypto_authenc_ctx *ctx = crypto_aead_ctx(authenc);
-       struct ablkcipher_request *abreq = aead_request_ctx(req);
+       struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
        struct crypto_ablkcipher *enc = ctx->enc;
        struct scatterlist *dst = req->dst;
        unsigned int cryptlen = req->cryptlen;
-       u8 *iv = (u8 *)(abreq + 1) + crypto_ablkcipher_reqsize(enc);
+       struct ablkcipher_request *abreq = (void *)(areq_ctx->tail
+                                                   + ctx->reqoff);
+       u8 *iv = (u8 *)abreq - crypto_ablkcipher_ivsize(enc);
        int err;
 
        ablkcipher_request_set_tfm(abreq, enc);
@@ -454,7 +456,7 @@ static int crypto_authenc_verify(struct aead_request *req,
        unsigned int authsize;
 
        areq_ctx->complete = authenc_verify_ahash_done;
-       areq_ctx->complete = authenc_verify_ahash_update_done;
+       areq_ctx->update_complete = authenc_verify_ahash_update_done;
 
        ohash = authenc_ahash_fn(req, CRYPTO_TFM_REQ_MAY_SLEEP);
        if (IS_ERR(ohash))
@@ -546,10 +548,6 @@ static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
        if (IS_ERR(auth))
                return PTR_ERR(auth);
 
-       ctx->reqoff = ALIGN(2 * crypto_ahash_digestsize(auth) +
-                           crypto_ahash_alignmask(auth),
-                           crypto_ahash_alignmask(auth) + 1);
-
        enc = crypto_spawn_skcipher(&ictx->enc);
        err = PTR_ERR(enc);
        if (IS_ERR(enc))
@@ -558,13 +556,18 @@ static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
        ctx->auth = auth;
        ctx->enc = enc;
 
-       tfm->crt_aead.reqsize = max_t(unsigned int,
-                               crypto_ahash_reqsize(auth) + ctx->reqoff +
-                               sizeof(struct authenc_request_ctx) +
+       ctx->reqoff = ALIGN(2 * crypto_ahash_digestsize(auth) +
+                           crypto_ahash_alignmask(auth),
+                           crypto_ahash_alignmask(auth) + 1) +
+                     crypto_ablkcipher_ivsize(enc);
+
+       tfm->crt_aead.reqsize = sizeof(struct authenc_request_ctx) +
+                               ctx->reqoff +
+                               max_t(unsigned int,
+                               crypto_ahash_reqsize(auth) +
                                sizeof(struct ahash_request),
                                sizeof(struct skcipher_givcrypt_request) +
-                               crypto_ablkcipher_reqsize(enc) +
-                               crypto_ablkcipher_ivsize(enc));
+                               crypto_ablkcipher_reqsize(enc));
 
        return 0;
 
index 9fda213a592ef6ccb7e709769f56b1b5f761d447..30efc7dad89173e86fcf65b6ac7c20d858abfeb2 100644 (file)
@@ -234,6 +234,7 @@ static struct shash_alg alg = {
        .export         =       md5_export,
        .import         =       md5_import,
        .descsize       =       sizeof(struct md5_state),
+       .statesize      =       sizeof(struct md5_state),
        .base           =       {
                .cra_name       =       "md5",
                .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
index 368ae6d3a096f4fb8f9efd34a1edfe5200bbbb30..a2b902f4d43706334e87b30656b1addc399b1c77 100644 (file)
@@ -96,8 +96,6 @@ source "drivers/edac/Kconfig"
 
 source "drivers/rtc/Kconfig"
 
-source "drivers/clocksource/Kconfig"
-
 source "drivers/dma/Kconfig"
 
 source "drivers/dca/Kconfig"
index 9863c98c81baaf071dc138fd4bc2dfa125b8481b..e9b7b402dbfb18d59f94786fad93e87dd74dd3a7 100644 (file)
@@ -123,6 +123,8 @@ static const struct file_operations acpi_processor_info_fops = {
 #endif
 
 DEFINE_PER_CPU(struct acpi_processor *, processors);
+EXPORT_PER_CPU_SYMBOL(processors);
+
 struct acpi_processor_errata errata __read_mostly;
 
 /* --------------------------------------------------------------------------
index 3ce3519e8f305d887f5f7fe6e2f3ed59c34cbe0a..89de75325ceabfa1336cce34dd481d3ebf0dc476 100644 (file)
@@ -1,6 +1,7 @@
 obj-$(CONFIG_PM)       += sysfs.o
 obj-$(CONFIG_PM_SLEEP) += main.o
 obj-$(CONFIG_PM_RUNTIME)       += runtime.o
+obj-$(CONFIG_PM_OPS)   += generic_ops.o
 obj-$(CONFIG_PM_TRACE_RTC)     += trace.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c
new file mode 100644 (file)
index 0000000..4b29d49
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * drivers/base/power/generic_ops.c - Generic PM callbacks for subsystems
+ *
+ * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#ifdef CONFIG_PM_RUNTIME
+/**
+ * pm_generic_runtime_idle - Generic runtime idle callback for subsystems.
+ * @dev: Device to handle.
+ *
+ * If PM operations are defined for the @dev's driver and they include
+ * ->runtime_idle(), execute it and return its error code, if nonzero.
+ * Otherwise, execute pm_runtime_suspend() for the device and return 0.
+ */
+int pm_generic_runtime_idle(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+       if (pm && pm->runtime_idle) {
+               int ret = pm->runtime_idle(dev);
+               if (ret)
+                       return ret;
+       }
+
+       pm_runtime_suspend(dev);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pm_generic_runtime_idle);
+
+/**
+ * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
+ * @dev: Device to suspend.
+ *
+ * If PM operations are defined for the @dev's driver and they include
+ * ->runtime_suspend(), execute it and return its error code.  Otherwise,
+ * return -EINVAL.
+ */
+int pm_generic_runtime_suspend(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       int ret;
+
+       ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : -EINVAL;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
+
+/**
+ * pm_generic_runtime_resume - Generic runtime resume callback for subsystems.
+ * @dev: Device to resume.
+ *
+ * If PM operations are defined for the @dev's driver and they include
+ * ->runtime_resume(), execute it and return its error code.  Otherwise,
+ * return -EINVAL.
+ */
+int pm_generic_runtime_resume(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       int ret;
+
+       ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : -EINVAL;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
+ * @dev: Device to handle.
+ * @event: PM transition of the system under way.
+ *
+ * If the device has not been suspended at run time, execute the
+ * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
+ * return its error code.  Otherwise, return zero.
+ */
+static int __pm_generic_call(struct device *dev, int event)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       int (*callback)(struct device *);
+
+       if (!pm || pm_runtime_suspended(dev))
+               return 0;
+
+       switch (event) {
+       case PM_EVENT_SUSPEND:
+               callback = pm->suspend;
+               break;
+       case PM_EVENT_FREEZE:
+               callback = pm->freeze;
+               break;
+       case PM_EVENT_HIBERNATE:
+               callback = pm->poweroff;
+               break;
+       case PM_EVENT_THAW:
+               callback = pm->thaw;
+               break;
+       default:
+               callback = NULL;
+               break;
+       }
+
+       return callback ? callback(dev) : 0;
+}
+
+/**
+ * pm_generic_suspend - Generic suspend callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend(struct device *dev)
+{
+       return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend);
+
+/**
+ * pm_generic_freeze - Generic freeze callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze(struct device *dev)
+{
+       return __pm_generic_call(dev, PM_EVENT_FREEZE);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze);
+
+/**
+ * pm_generic_poweroff - Generic poweroff callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff(struct device *dev)
+{
+       return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff);
+
+/**
+ * pm_generic_thaw - Generic thaw callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw(struct device *dev)
+{
+       return __pm_generic_call(dev, PM_EVENT_THAW);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw);
+
+/**
+ * __pm_generic_resume - Generic resume/restore callback for subsystems.
+ * @dev: Device to handle.
+ * @event: PM transition of the system under way.
+ *
+ * Execute the resume/resotre callback provided by the @dev's driver, if
+ * defined.  If it returns 0, change the device's runtime PM status to 'active'.
+ * Return the callback's error code.
+ */
+static int __pm_generic_resume(struct device *dev, int event)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       int (*callback)(struct device *);
+       int ret;
+
+       if (!pm)
+               return 0;
+
+       switch (event) {
+       case PM_EVENT_RESUME:
+               callback = pm->resume;
+               break;
+       case PM_EVENT_RESTORE:
+               callback = pm->restore;
+               break;
+       default:
+               callback = NULL;
+               break;
+       }
+
+       if (!callback)
+               return 0;
+
+       ret = callback(dev);
+       if (!ret) {
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
+
+       return ret;
+}
+
+/**
+ * pm_generic_resume - Generic resume callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume(struct device *dev)
+{
+       return __pm_generic_resume(dev, PM_EVENT_RESUME);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume);
+
+/**
+ * pm_generic_restore - Generic restore callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore(struct device *dev)
+{
+       return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore);
+#endif /* CONFIG_PM_SLEEP */
+
+struct dev_pm_ops generic_subsys_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+       .suspend = pm_generic_suspend,
+       .resume = pm_generic_resume,
+       .freeze = pm_generic_freeze,
+       .thaw = pm_generic_thaw,
+       .poweroff = pm_generic_poweroff,
+       .restore = pm_generic_restore,
+#endif
+#ifdef CONFIG_PM_RUNTIME
+       .runtime_suspend = pm_generic_runtime_suspend,
+       .runtime_resume = pm_generic_runtime_resume,
+       .runtime_idle = pm_generic_runtime_idle,
+#endif
+};
+EXPORT_SYMBOL_GPL(generic_subsys_pm_ops);
index 8a713f1e965351ef91a2d885485ec5fe4e96c719..919a28558d362fa152ae3e12d94babb1cecbfd60 100644 (file)
@@ -11,6 +11,9 @@
 #include <asm/smp.h>
 #include "agp.h"
 
+int intel_agp_enabled;
+EXPORT_SYMBOL(intel_agp_enabled);
+
 /*
  * If we have Intel graphics, we're not going to have anything other than
  * an Intel IOMMU. So make the correct use of the PCI DMA API contingent
 #define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB         0x0062
 #define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB    0x006a
 #define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG          0x0046
+#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB  0x0100
+#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG  0x0102
+#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB  0x0104
+#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG  0x0106
 
 /* cover 915 and 945 variants */
 #define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB)
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB)
 
 extern int agp_memory_reserved;
 
@@ -148,6 +157,25 @@ extern int agp_memory_reserved;
 #define INTEL_I7505_AGPCTRL    0x70
 #define INTEL_I7505_MCHCFG     0x50
 
+#define SNB_GMCH_CTRL  0x50
+#define SNB_GMCH_GMS_STOLEN_MASK       0xF8
+#define SNB_GMCH_GMS_STOLEN_32M                (1 << 3)
+#define SNB_GMCH_GMS_STOLEN_64M                (2 << 3)
+#define SNB_GMCH_GMS_STOLEN_96M                (3 << 3)
+#define SNB_GMCH_GMS_STOLEN_128M       (4 << 3)
+#define SNB_GMCH_GMS_STOLEN_160M       (5 << 3)
+#define SNB_GMCH_GMS_STOLEN_192M       (6 << 3)
+#define SNB_GMCH_GMS_STOLEN_224M       (7 << 3)
+#define SNB_GMCH_GMS_STOLEN_256M       (8 << 3)
+#define SNB_GMCH_GMS_STOLEN_288M       (9 << 3)
+#define SNB_GMCH_GMS_STOLEN_320M       (0xa << 3)
+#define SNB_GMCH_GMS_STOLEN_352M       (0xb << 3)
+#define SNB_GMCH_GMS_STOLEN_384M       (0xc << 3)
+#define SNB_GMCH_GMS_STOLEN_416M       (0xd << 3)
+#define SNB_GMCH_GMS_STOLEN_448M       (0xe << 3)
+#define SNB_GMCH_GMS_STOLEN_480M       (0xf << 3)
+#define SNB_GMCH_GMS_STOLEN_512M       (0x10 << 3)
+
 static const struct aper_size_info_fixed intel_i810_sizes[] =
 {
        {64, 16384, 4},
@@ -294,6 +322,13 @@ static void intel_agp_insert_sg_entries(struct agp_memory *mem,
                                        off_t pg_start, int mask_type)
 {
        int i, j;
+       u32 cache_bits = 0;
+
+       if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB ||
+           agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB)
+       {
+               cache_bits = I830_PTE_SYSTEM_CACHED;
+       }
 
        for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
                writel(agp_bridge->driver->mask_memory(agp_bridge,
@@ -614,7 +649,7 @@ static struct aper_size_info_fixed intel_i830_sizes[] =
 static void intel_i830_init_gtt_entries(void)
 {
        u16 gmch_ctrl;
-       int gtt_entries;
+       int gtt_entries = 0;
        u8 rdct;
        int local = 0;
        static const int ddt[4] = { 0, 16, 32, 64 };
@@ -706,6 +741,63 @@ static void intel_i830_init_gtt_entries(void)
                        gtt_entries = 0;
                        break;
                }
+       } else if (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB ||
+                  agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB) {
+               /*
+                * SandyBridge has new memory control reg at 0x50.w
+                */
+               u16 snb_gmch_ctl;
+               pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl);
+               switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) {
+               case SNB_GMCH_GMS_STOLEN_32M:
+                       gtt_entries = MB(32) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_64M:
+                       gtt_entries = MB(64) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_96M:
+                       gtt_entries = MB(96) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_128M:
+                       gtt_entries = MB(128) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_160M:
+                       gtt_entries = MB(160) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_192M:
+                       gtt_entries = MB(192) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_224M:
+                       gtt_entries = MB(224) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_256M:
+                       gtt_entries = MB(256) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_288M:
+                       gtt_entries = MB(288) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_320M:
+                       gtt_entries = MB(320) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_352M:
+                       gtt_entries = MB(352) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_384M:
+                       gtt_entries = MB(384) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_416M:
+                       gtt_entries = MB(416) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_448M:
+                       gtt_entries = MB(448) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_480M:
+                       gtt_entries = MB(480) - KB(size);
+                       break;
+               case SNB_GMCH_GMS_STOLEN_512M:
+                       gtt_entries = MB(512) - KB(size);
+                       break;
+               }
        } else {
                switch (gmch_ctrl & I855_GMCH_GMS_MASK) {
                case I855_GMCH_GMS_STOLEN_1M:
@@ -1357,6 +1449,8 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
        case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB:
        case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB:
        case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB:
+       case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB:
+       case PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB:
                *gtt_offset = *gtt_size = MB(2);
                break;
        default:
@@ -2338,9 +2432,9 @@ static const struct intel_driver_description {
                NULL, &intel_g33_driver },
        { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33",
                NULL, &intel_g33_driver },
-       { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "Pineview",
+       { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "GMA3150",
                NULL, &intel_g33_driver },
-       { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "Pineview",
+       { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "GMA3150",
                NULL, &intel_g33_driver },
        { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0,
            "GM45", NULL, &intel_i965_driver },
@@ -2355,13 +2449,17 @@ static const struct intel_driver_description {
        { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0,
            "G41", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, 0,
-           "Ironlake/D", NULL, &intel_i965_driver },
+           "HD Graphics", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0,
-           "Ironlake/M", NULL, &intel_i965_driver },
+           "HD Graphics", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0,
-           "Ironlake/MA", NULL, &intel_i965_driver },
+           "HD Graphics", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0,
-           "Ironlake/MC2", NULL, &intel_i965_driver },
+           "HD Graphics", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_IG, 0,
+           "Sandybridge", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB, PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_IG, 0,
+           "Sandybridge", NULL, &intel_i965_driver },
        { 0, 0, 0, NULL, NULL, NULL }
 };
 
@@ -2371,7 +2469,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev,
        struct agp_bridge_data *bridge;
        u8 cap_ptr = 0;
        struct resource *r;
-       int i;
+       int i, err;
 
        cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
 
@@ -2463,7 +2561,10 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev,
        }
 
        pci_set_drvdata(pdev, bridge);
-       return agp_add_bridge(bridge);
+       err = agp_add_bridge(bridge);
+       if (!err)
+               intel_agp_enabled = 1;
+       return err;
 }
 
 static void __devexit agp_intel_remove(struct pci_dev *pdev)
@@ -2568,6 +2669,8 @@ static struct pci_device_id agp_intel_pci_table[] = {
        ID(PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB),
        ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB),
        ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB),
+       ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB),
+       ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB),
        { }
 };
 
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
deleted file mode 100644 (file)
index 08f726c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-config CS5535_CLOCK_EVENT_SRC
-       tristate "CS5535/CS5536 high-res timer (MFGPT) events"
-       depends on GENERIC_TIME && GENERIC_CLOCKEVENTS && CS5535_MFGPT
-       help
-         This driver provides a clock event source based on the MFGPT
-         timer(s) in the CS5535 and CS5536 companion chips.
-         MFGPTs have a better resolution and max interval than the
-         generic PIT, and are suitable for use as high-res timers.
-
index 73655aeb3a60993e28fbbf9ea7930128e5a23407..1aea7157d8fffa603ca79d4cca7552db0bb313bd 100644 (file)
@@ -100,8 +100,8 @@ struct menu_device {
        int             needs_update;
 
        unsigned int    expected_us;
-       u64             predicted_us;
        unsigned int    measured_us;
+       u64             predicted_us;
        unsigned int    exit_us;
        unsigned int    bucket;
        u64             correction_factor[BUCKETS];
index e02d74b1e8922ae44a3817699937b6a08090e038..c27f80e5d5319e25a9e9a777b362bb52329b98d9 100644 (file)
@@ -13,6 +13,22 @@ menuconfig DMADEVICES
          DMA Device drivers supported by the configured arch, it may
          be empty in some cases.
 
+config DMADEVICES_DEBUG
+        bool "DMA Engine debugging"
+        depends on DMADEVICES != n
+        help
+          This is an option for use by developers; most people should
+          say N here.  This enables DMA engine core and driver debugging.
+
+config DMADEVICES_VDEBUG
+        bool "DMA Engine verbose debugging"
+        depends on DMADEVICES_DEBUG != n
+        help
+          This is an option for use by developers; most people should
+          say N here.  This enables deeper (more verbose) debugging of
+          the DMA engine core and drivers.
+
+
 if DMADEVICES
 
 comment "DMA Devices"
@@ -69,6 +85,13 @@ config FSL_DMA
          The Elo is the DMA controller on some 82xx and 83xx parts, and the
          Elo Plus is the DMA controller on 85xx and 86xx parts.
 
+config MPC512X_DMA
+       tristate "Freescale MPC512x built-in DMA engine support"
+       depends on PPC_MPC512x
+       select DMA_ENGINE
+       ---help---
+         Enable support for the Freescale MPC512x built-in DMA engine.
+
 config MV_XOR
        bool "Marvell XOR engine support"
        depends on PLAT_ORION
index 807053d48232e61e8aa56a2ea79a91a6c797f4e3..22bba3d5e2b69a0a9a536aaf7df657b6eeb4932d 100644 (file)
@@ -1,9 +1,17 @@
+ifeq ($(CONFIG_DMADEVICES_DEBUG),y)
+       EXTRA_CFLAGS    += -DDEBUG
+endif
+ifeq ($(CONFIG_DMADEVICES_VDEBUG),y)
+       EXTRA_CFLAGS    += -DVERBOSE_DEBUG
+endif
+
 obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
 obj-$(CONFIG_NET_DMA) += iovlock.o
 obj-$(CONFIG_DMATEST) += dmatest.o
 obj-$(CONFIG_INTEL_IOATDMA) += ioat/
 obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o
 obj-$(CONFIG_FSL_DMA) += fsldma.o
+obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o
 obj-$(CONFIG_MV_XOR) += mv_xor.o
 obj-$(CONFIG_DW_DMAC) += dw_dmac.o
 obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
index 64a937262a401815179d8ea7e3295b6438ce6110..1656fdcdb6c244cd8ed73b8d212790f8ebde8c5c 100644 (file)
@@ -39,7 +39,6 @@ struct coh901318_desc {
        unsigned int sg_len;
        struct coh901318_lli *data;
        enum dma_data_direction dir;
-       int pending_irqs;
        unsigned long flags;
 };
 
@@ -72,7 +71,6 @@ struct coh901318_chan {
 
        unsigned long nbr_active_done;
        unsigned long busy;
-       int pending_irqs;
 
        struct coh901318_base *base;
 };
@@ -80,18 +78,16 @@ struct coh901318_chan {
 static void coh901318_list_print(struct coh901318_chan *cohc,
                                 struct coh901318_lli *lli)
 {
-       struct coh901318_lli *l;
-       dma_addr_t addr =  virt_to_phys(lli);
+       struct coh901318_lli *l = lli;
        int i = 0;
 
-       while (addr) {
-               l = phys_to_virt(addr);
+       while (l) {
                dev_vdbg(COHC_2_DEV(cohc), "i %d, lli %p, ctrl 0x%x, src 0x%x"
-                        ", dst 0x%x, link 0x%x link_virt 0x%p\n",
+                        ", dst 0x%x, link 0x%x virt_link_addr 0x%p\n",
                         i, l, l->control, l->src_addr, l->dst_addr,
-                        l->link_addr, phys_to_virt(l->link_addr));
+                        l->link_addr, l->virt_link_addr);
                i++;
-               addr = l->link_addr;
+               l = l->virt_link_addr;
        }
 }
 
@@ -125,7 +121,7 @@ static int coh901318_debugfs_read(struct file *file, char __user *buf,
                goto err_kmalloc;
        tmp = dev_buf;
 
-       tmp += sprintf(tmp, "DMA -- enable dma channels\n");
+       tmp += sprintf(tmp, "DMA -- enabled dma channels\n");
 
        for (i = 0; i < debugfs_dma_base->platform->max_channels; i++)
                if (started_channels & (1 << i))
@@ -337,16 +333,22 @@ coh901318_desc_get(struct coh901318_chan *cohc)
                 * TODO: alloc a pile of descs instead of just one,
                 * avoid many small allocations.
                 */
-               desc = kmalloc(sizeof(struct coh901318_desc), GFP_NOWAIT);
+               desc = kzalloc(sizeof(struct coh901318_desc), GFP_NOWAIT);
                if (desc == NULL)
                        goto out;
                INIT_LIST_HEAD(&desc->node);
+               dma_async_tx_descriptor_init(&desc->desc, &cohc->chan);
        } else {
                /* Reuse an old desc. */
                desc = list_first_entry(&cohc->free,
                                        struct coh901318_desc,
                                        node);
                list_del(&desc->node);
+               /* Initialize it a bit so it's not insane */
+               desc->sg = NULL;
+               desc->sg_len = 0;
+               desc->desc.callback = NULL;
+               desc->desc.callback_param = NULL;
        }
 
  out:
@@ -364,10 +366,6 @@ static void
 coh901318_desc_submit(struct coh901318_chan *cohc, struct coh901318_desc *desc)
 {
        list_add_tail(&desc->node, &cohc->active);
-
-       BUG_ON(cohc->pending_irqs != 0);
-
-       cohc->pending_irqs = desc->pending_irqs;
 }
 
 static struct coh901318_desc *
@@ -592,6 +590,10 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc)
        return cohd_que;
 }
 
+/*
+ * This tasklet is called from the interrupt handler to
+ * handle each descriptor (DMA job) that is sent to a channel.
+ */
 static void dma_tasklet(unsigned long data)
 {
        struct coh901318_chan *cohc = (struct coh901318_chan *) data;
@@ -600,55 +602,58 @@ static void dma_tasklet(unsigned long data)
        dma_async_tx_callback callback;
        void *callback_param;
 
+       dev_vdbg(COHC_2_DEV(cohc), "[%s] chan_id %d"
+                " nbr_active_done %ld\n", __func__,
+                cohc->id, cohc->nbr_active_done);
+
        spin_lock_irqsave(&cohc->lock, flags);
 
-       /* get first active entry from list */
+       /* get first active descriptor entry from list */
        cohd_fin = coh901318_first_active_get(cohc);
 
-       BUG_ON(cohd_fin->pending_irqs == 0);
-
        if (cohd_fin == NULL)
                goto err;
 
-       cohd_fin->pending_irqs--;
-       cohc->completed = cohd_fin->desc.cookie;
+       /* locate callback to client */
+       callback = cohd_fin->desc.callback;
+       callback_param = cohd_fin->desc.callback_param;
 
-       if (cohc->nbr_active_done == 0)
-               return;
+       /* sign this job as completed on the channel */
+       cohc->completed = cohd_fin->desc.cookie;
 
-       if (!cohd_fin->pending_irqs) {
-               /* release the lli allocation*/
-               coh901318_lli_free(&cohc->base->pool, &cohd_fin->data);
-       }
+       /* release the lli allocation and remove the descriptor */
+       coh901318_lli_free(&cohc->base->pool, &cohd_fin->data);
 
-       dev_vdbg(COHC_2_DEV(cohc), "[%s] chan_id %d pending_irqs %d"
-                " nbr_active_done %ld\n", __func__,
-                cohc->id, cohc->pending_irqs, cohc->nbr_active_done);
+       /* return desc to free-list */
+       coh901318_desc_remove(cohd_fin);
+       coh901318_desc_free(cohc, cohd_fin);
 
-       /* callback to client */
-       callback = cohd_fin->desc.callback;
-       callback_param = cohd_fin->desc.callback_param;
-
-       if (!cohd_fin->pending_irqs) {
-               coh901318_desc_remove(cohd_fin);
+       spin_unlock_irqrestore(&cohc->lock, flags);
 
-               /* return desc to free-list */
-               coh901318_desc_free(cohc, cohd_fin);
-       }
+       /* Call the callback when we're done */
+       if (callback)
+               callback(callback_param);
 
-       if (cohc->nbr_active_done)
-               cohc->nbr_active_done--;
+       spin_lock_irqsave(&cohc->lock, flags);
 
+       /*
+        * If another interrupt fired while the tasklet was scheduling,
+        * we don't get called twice, so we have this number of active
+        * counter that keep track of the number of IRQs expected to
+        * be handled for this channel. If there happen to be more than
+        * one IRQ to be ack:ed, we simply schedule this tasklet again.
+        */
+       cohc->nbr_active_done--;
        if (cohc->nbr_active_done) {
+               dev_dbg(COHC_2_DEV(cohc), "scheduling tasklet again, new IRQs "
+                       "came in while we were scheduling this tasklet\n");
                if (cohc_chan_conf(cohc)->priority_high)
                        tasklet_hi_schedule(&cohc->tasklet);
                else
                        tasklet_schedule(&cohc->tasklet);
        }
-       spin_unlock_irqrestore(&cohc->lock, flags);
 
-       if (callback)
-               callback(callback_param);
+       spin_unlock_irqrestore(&cohc->lock, flags);
 
        return;
 
@@ -667,16 +672,17 @@ static void dma_tc_handle(struct coh901318_chan *cohc)
        if (!cohc->allocated)
                return;
 
-       BUG_ON(cohc->pending_irqs == 0);
+       spin_lock(&cohc->lock);
 
-       cohc->pending_irqs--;
        cohc->nbr_active_done++;
 
-       if (cohc->pending_irqs == 0 && coh901318_queue_start(cohc) == NULL)
+       if (coh901318_queue_start(cohc) == NULL)
                cohc->busy = 0;
 
        BUG_ON(list_empty(&cohc->active));
 
+       spin_unlock(&cohc->lock);
+
        if (cohc_chan_conf(cohc)->priority_high)
                tasklet_hi_schedule(&cohc->tasklet);
        else
@@ -870,6 +876,7 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
        struct coh901318_chan *cohc = to_coh901318_chan(chan);
        int lli_len;
        u32 ctrl_last = cohc_chan_param(cohc)->ctrl_lli_last;
+       int ret;
 
        spin_lock_irqsave(&cohc->lock, flg);
 
@@ -890,22 +897,19 @@ coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
        if (data == NULL)
                goto err;
 
-       cohd = coh901318_desc_get(cohc);
-       cohd->sg = NULL;
-       cohd->sg_len = 0;
-       cohd->data = data;
-
-       cohd->pending_irqs =
-               coh901318_lli_fill_memcpy(
-                               &cohc->base->pool, data, src, size, dest,
-                               cohc_chan_param(cohc)->ctrl_lli_chained,
-                               ctrl_last);
-       cohd->flags = flags;
+       ret = coh901318_lli_fill_memcpy(
+               &cohc->base->pool, data, src, size, dest,
+               cohc_chan_param(cohc)->ctrl_lli_chained,
+               ctrl_last);
+       if (ret)
+               goto err;
 
        COH_DBG(coh901318_list_print(cohc, data));
 
-       dma_async_tx_descriptor_init(&cohd->desc, chan);
-
+       /* Pick a descriptor to handle this transfer */
+       cohd = coh901318_desc_get(cohc);
+       cohd->data = data;
+       cohd->flags = flags;
        cohd->desc.tx_submit = coh901318_tx_submit;
 
        spin_unlock_irqrestore(&cohc->lock, flg);
@@ -924,6 +928,7 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        struct coh901318_chan *cohc = to_coh901318_chan(chan);
        struct coh901318_lli *data;
        struct coh901318_desc *cohd;
+       const struct coh901318_params *params;
        struct scatterlist *sg;
        int len = 0;
        int size;
@@ -931,7 +936,9 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        u32 ctrl_chained = cohc_chan_param(cohc)->ctrl_lli_chained;
        u32 ctrl = cohc_chan_param(cohc)->ctrl_lli;
        u32 ctrl_last = cohc_chan_param(cohc)->ctrl_lli_last;
+       u32 config;
        unsigned long flg;
+       int ret;
 
        if (!sgl)
                goto out;
@@ -947,15 +954,14 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                /* Trigger interrupt after last lli */
                ctrl_last |= COH901318_CX_CTRL_TC_IRQ_ENABLE;
 
-       cohd = coh901318_desc_get(cohc);
-       cohd->sg = NULL;
-       cohd->sg_len = 0;
-       cohd->dir = direction;
+       params = cohc_chan_param(cohc);
+       config = params->config;
 
        if (direction == DMA_TO_DEVICE) {
                u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE |
                        COH901318_CX_CTRL_SRC_ADDR_INC_ENABLE;
 
+               config |= COH901318_CX_CFG_RM_MEMORY_TO_PRIMARY;
                ctrl_chained |= tx_flags;
                ctrl_last |= tx_flags;
                ctrl |= tx_flags;
@@ -963,16 +969,14 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                u32 rx_flags = COH901318_CX_CTRL_PRDD_DEST |
                        COH901318_CX_CTRL_DST_ADDR_INC_ENABLE;
 
+               config |= COH901318_CX_CFG_RM_PRIMARY_TO_MEMORY;
                ctrl_chained |= rx_flags;
                ctrl_last |= rx_flags;
                ctrl |= rx_flags;
        } else
                goto err_direction;
 
-       dma_async_tx_descriptor_init(&cohd->desc, chan);
-
-       cohd->desc.tx_submit = coh901318_tx_submit;
-
+       coh901318_set_conf(cohc, config);
 
        /* The dma only supports transmitting packages up to
         * MAX_DMA_PACKET_SIZE. Calculate to total number of
@@ -994,32 +998,37 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                len += factor;
        }
 
+       pr_debug("Allocate %d lli:s for this transfer\n", len);
        data = coh901318_lli_alloc(&cohc->base->pool, len);
 
        if (data == NULL)
                goto err_dma_alloc;
 
        /* initiate allocated data list */
-       cohd->pending_irqs =
-               coh901318_lli_fill_sg(&cohc->base->pool, data, sgl, sg_len,
-                                     cohc_dev_addr(cohc),
-                                     ctrl_chained,
-                                     ctrl,
-                                     ctrl_last,
-                                     direction, COH901318_CX_CTRL_TC_IRQ_ENABLE);
-       cohd->data = data;
-
-       cohd->flags = flags;
+       ret = coh901318_lli_fill_sg(&cohc->base->pool, data, sgl, sg_len,
+                                   cohc_dev_addr(cohc),
+                                   ctrl_chained,
+                                   ctrl,
+                                   ctrl_last,
+                                   direction, COH901318_CX_CTRL_TC_IRQ_ENABLE);
+       if (ret)
+               goto err_lli_fill;
 
        COH_DBG(coh901318_list_print(cohc, data));
 
+       /* Pick a descriptor to handle this transfer */
+       cohd = coh901318_desc_get(cohc);
+       cohd->dir = direction;
+       cohd->flags = flags;
+       cohd->desc.tx_submit = coh901318_tx_submit;
+       cohd->data = data;
+
        spin_unlock_irqrestore(&cohc->lock, flg);
 
        return &cohd->desc;
+ err_lli_fill:
  err_dma_alloc:
  err_direction:
-       coh901318_desc_remove(cohd);
-       coh901318_desc_free(cohc, cohd);
        spin_unlock_irqrestore(&cohc->lock, flg);
  out:
        return NULL;
@@ -1092,9 +1101,8 @@ coh901318_terminate_all(struct dma_chan *chan)
                /* release the lli allocation*/
                coh901318_lli_free(&cohc->base->pool, &cohd->data);
 
-               coh901318_desc_remove(cohd);
-
                /* return desc to free-list */
+               coh901318_desc_remove(cohd);
                coh901318_desc_free(cohc, cohd);
        }
 
@@ -1102,16 +1110,14 @@ coh901318_terminate_all(struct dma_chan *chan)
                /* release the lli allocation*/
                coh901318_lli_free(&cohc->base->pool, &cohd->data);
 
-               coh901318_desc_remove(cohd);
-
                /* return desc to free-list */
+               coh901318_desc_remove(cohd);
                coh901318_desc_free(cohc, cohd);
        }
 
 
        cohc->nbr_active_done = 0;
        cohc->busy = 0;
-       cohc->pending_irqs = 0;
 
        spin_unlock_irqrestore(&cohc->lock, flags);
 }
@@ -1138,7 +1144,6 @@ void coh901318_base_init(struct dma_device *dma, const int *pick_chans,
 
                        spin_lock_init(&cohc->lock);
 
-                       cohc->pending_irqs = 0;
                        cohc->nbr_active_done = 0;
                        cohc->busy = 0;
                        INIT_LIST_HEAD(&cohc->free);
@@ -1254,12 +1259,17 @@ static int __init coh901318_probe(struct platform_device *pdev)
        base->dma_memcpy.device_issue_pending = coh901318_issue_pending;
        base->dma_memcpy.device_terminate_all = coh901318_terminate_all;
        base->dma_memcpy.dev = &pdev->dev;
+       /*
+        * This controller can only access address at even 32bit boundaries,
+        * i.e. 2^2
+        */
+       base->dma_memcpy.copy_align = 2;
        err = dma_async_device_register(&base->dma_memcpy);
 
        if (err)
                goto err_register_memcpy;
 
-       dev_dbg(&pdev->dev, "Initialized COH901318 DMA on virtual base 0x%08x\n",
+       dev_info(&pdev->dev, "Initialized COH901318 DMA on virtual base 0x%08x\n",
                (u32) base->virtbase);
 
        return err;
index f5120f238a4dd03f74dcfb4b931c1078c423bb58..71d58c1a1e862409287ccecb5d874c51fd567e34 100644 (file)
@@ -74,6 +74,8 @@ coh901318_lli_alloc(struct coh901318_pool *pool, unsigned int len)
 
        lli = head;
        lli->phy_this = phy;
+       lli->link_addr = 0x00000000;
+       lli->virt_link_addr = 0x00000000U;
 
        for (i = 1; i < len; i++) {
                lli_prev = lli;
@@ -85,13 +87,13 @@ coh901318_lli_alloc(struct coh901318_pool *pool, unsigned int len)
 
                DEBUGFS_POOL_COUNTER_ADD(pool, 1);
                lli->phy_this = phy;
+               lli->link_addr = 0x00000000;
+               lli->virt_link_addr = 0x00000000U;
 
                lli_prev->link_addr = phy;
                lli_prev->virt_link_addr = lli;
        }
 
-       lli->link_addr = 0x00000000U;
-
        spin_unlock(&pool->lock);
 
        return head;
@@ -166,8 +168,7 @@ coh901318_lli_fill_memcpy(struct coh901318_pool *pool,
        lli->src_addr = src;
        lli->dst_addr = dst;
 
-       /* One irq per single transfer */
-       return 1;
+       return 0;
 }
 
 int
@@ -223,8 +224,7 @@ coh901318_lli_fill_single(struct coh901318_pool *pool,
        lli->src_addr = src;
        lli->dst_addr = dst;
 
-       /* One irq per single transfer */
-       return 1;
+       return 0;
 }
 
 int
@@ -240,7 +240,6 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool,
        u32 ctrl_sg;
        dma_addr_t src = 0;
        dma_addr_t dst = 0;
-       int nbr_of_irq = 0;
        u32 bytes_to_transfer;
        u32 elem_size;
 
@@ -269,15 +268,12 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool,
                        ctrl_sg = ctrl ? ctrl : ctrl_last;
 
 
-               if ((ctrl_sg & ctrl_irq_mask))
-                       nbr_of_irq++;
-
                if (dir == DMA_TO_DEVICE)
                        /* increment source address */
-                       src = sg_dma_address(sg);
+                       src = sg_phys(sg);
                else
                        /* increment destination address */
-                       dst =  sg_dma_address(sg);
+                       dst =  sg_phys(sg);
 
                bytes_to_transfer = sg_dma_len(sg);
 
@@ -310,8 +306,7 @@ coh901318_lli_fill_sg(struct coh901318_pool *pool,
        }
        spin_unlock(&pool->lock);
 
-       /* There can be many IRQs per sg transfer */
-       return nbr_of_irq;
+       return 0;
  err:
        spin_unlock(&pool->lock);
        return -EINVAL;
index 948d563941c92e682ec7ed923cf829594597d037..6fa55fe3dd248034ef5dc3259755c2ce731be47e 100644 (file)
@@ -237,7 +237,7 @@ static int dmatest_func(void *data)
        dma_cookie_t            cookie;
        enum dma_status         status;
        enum dma_ctrl_flags     flags;
-       u8                      pq_coefs[pq_sources];
+       u8                      pq_coefs[pq_sources + 1];
        int                     ret;
        int                     src_cnt;
        int                     dst_cnt;
@@ -257,7 +257,7 @@ static int dmatest_func(void *data)
        } else if (thread->type == DMA_PQ) {
                src_cnt = pq_sources | 1; /* force odd to ensure dst = src */
                dst_cnt = 2;
-               for (i = 0; i < pq_sources; i++)
+               for (i = 0; i < src_cnt; i++)
                        pq_coefs[i] = 1;
        } else
                goto err_srcs;
@@ -347,7 +347,7 @@ static int dmatest_func(void *data)
                else if (thread->type == DMA_XOR)
                        tx = dev->device_prep_dma_xor(chan,
                                                      dma_dsts[0] + dst_off,
-                                                     dma_srcs, xor_sources,
+                                                     dma_srcs, src_cnt,
                                                      len, flags);
                else if (thread->type == DMA_PQ) {
                        dma_addr_t dma_pq[dst_cnt];
@@ -355,7 +355,7 @@ static int dmatest_func(void *data)
                        for (i = 0; i < dst_cnt; i++)
                                dma_pq[i] = dma_dsts[i] + dst_off;
                        tx = dev->device_prep_dma_pq(chan, dma_pq, dma_srcs,
-                                                    pq_sources, pq_coefs,
+                                                    src_cnt, pq_coefs,
                                                     len, flags);
                }
 
index 296f9e747fac3b920cc344e6862f6d20d7c4b6b8..bbb4be5a3ff493be3bf35ac07474e43002efd71d 100644 (file)
 #include <asm/fsldma.h>
 #include "fsldma.h"
 
-static void dma_init(struct fsl_dma_chan *fsl_chan)
+static void dma_init(struct fsldma_chan *chan)
 {
        /* Reset the channel */
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, 0, 32);
+       DMA_OUT(chan, &chan->regs->mr, 0, 32);
 
-       switch (fsl_chan->feature & FSL_DMA_IP_MASK) {
+       switch (chan->feature & FSL_DMA_IP_MASK) {
        case FSL_DMA_IP_85XX:
                /* Set the channel to below modes:
                 * EIE - Error interrupt enable
                 * EOSIE - End of segments interrupt enable (basic mode)
                 * EOLNIE - End of links interrupt enable
                 */
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EIE
+               DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EIE
                                | FSL_DMA_MR_EOLNIE | FSL_DMA_MR_EOSIE, 32);
                break;
        case FSL_DMA_IP_83XX:
@@ -57,170 +57,146 @@ static void dma_init(struct fsl_dma_chan *fsl_chan)
                 * EOTIE - End-of-transfer interrupt enable
                 * PRC_RM - PCI read multiple
                 */
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE
+               DMA_OUT(chan, &chan->regs->mr, FSL_DMA_MR_EOTIE
                                | FSL_DMA_MR_PRC_RM, 32);
                break;
        }
-
 }
 
-static void set_sr(struct fsl_dma_chan *fsl_chan, u32 val)
+static void set_sr(struct fsldma_chan *chan, u32 val)
 {
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->sr, val, 32);
+       DMA_OUT(chan, &chan->regs->sr, val, 32);
 }
 
-static u32 get_sr(struct fsl_dma_chan *fsl_chan)
+static u32 get_sr(struct fsldma_chan *chan)
 {
-       return DMA_IN(fsl_chan, &fsl_chan->reg_base->sr, 32);
+       return DMA_IN(chan, &chan->regs->sr, 32);
 }
 
-static void set_desc_cnt(struct fsl_dma_chan *fsl_chan,
+static void set_desc_cnt(struct fsldma_chan *chan,
                                struct fsl_dma_ld_hw *hw, u32 count)
 {
-       hw->count = CPU_TO_DMA(fsl_chan, count, 32);
+       hw->count = CPU_TO_DMA(chan, count, 32);
 }
 
-static void set_desc_src(struct fsl_dma_chan *fsl_chan,
+static void set_desc_src(struct fsldma_chan *chan,
                                struct fsl_dma_ld_hw *hw, dma_addr_t src)
 {
        u64 snoop_bits;
 
-       snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
+       snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
                ? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0;
-       hw->src_addr = CPU_TO_DMA(fsl_chan, snoop_bits | src, 64);
+       hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64);
 }
 
-static void set_desc_dest(struct fsl_dma_chan *fsl_chan,
-                               struct fsl_dma_ld_hw *hw, dma_addr_t dest)
+static void set_desc_dst(struct fsldma_chan *chan,
+                               struct fsl_dma_ld_hw *hw, dma_addr_t dst)
 {
        u64 snoop_bits;
 
-       snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
+       snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
                ? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0;
-       hw->dst_addr = CPU_TO_DMA(fsl_chan, snoop_bits | dest, 64);
+       hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64);
 }
 
-static void set_desc_next(struct fsl_dma_chan *fsl_chan,
+static void set_desc_next(struct fsldma_chan *chan,
                                struct fsl_dma_ld_hw *hw, dma_addr_t next)
 {
        u64 snoop_bits;
 
-       snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
+       snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
                ? FSL_DMA_SNEN : 0;
-       hw->next_ln_addr = CPU_TO_DMA(fsl_chan, snoop_bits | next, 64);
-}
-
-static void set_cdar(struct fsl_dma_chan *fsl_chan, dma_addr_t addr)
-{
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->cdar, addr | FSL_DMA_SNEN, 64);
+       hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64);
 }
 
-static dma_addr_t get_cdar(struct fsl_dma_chan *fsl_chan)
+static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
 {
-       return DMA_IN(fsl_chan, &fsl_chan->reg_base->cdar, 64) & ~FSL_DMA_SNEN;
+       DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
 }
 
-static void set_ndar(struct fsl_dma_chan *fsl_chan, dma_addr_t addr)
+static dma_addr_t get_cdar(struct fsldma_chan *chan)
 {
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->ndar, addr, 64);
+       return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
 }
 
-static dma_addr_t get_ndar(struct fsl_dma_chan *fsl_chan)
+static dma_addr_t get_ndar(struct fsldma_chan *chan)
 {
-       return DMA_IN(fsl_chan, &fsl_chan->reg_base->ndar, 64);
+       return DMA_IN(chan, &chan->regs->ndar, 64);
 }
 
-static u32 get_bcr(struct fsl_dma_chan *fsl_chan)
+static u32 get_bcr(struct fsldma_chan *chan)
 {
-       return DMA_IN(fsl_chan, &fsl_chan->reg_base->bcr, 32);
+       return DMA_IN(chan, &chan->regs->bcr, 32);
 }
 
-static int dma_is_idle(struct fsl_dma_chan *fsl_chan)
+static int dma_is_idle(struct fsldma_chan *chan)
 {
-       u32 sr = get_sr(fsl_chan);
+       u32 sr = get_sr(chan);
        return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH);
 }
 
-static void dma_start(struct fsl_dma_chan *fsl_chan)
+static void dma_start(struct fsldma_chan *chan)
 {
-       u32 mr_set = 0;
-
-       if (fsl_chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->bcr, 0, 32);
-               mr_set |= FSL_DMA_MR_EMP_EN;
-       } else if ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-                       DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
-                               & ~FSL_DMA_MR_EMP_EN, 32);
+       u32 mode;
+
+       mode = DMA_IN(chan, &chan->regs->mr, 32);
+
+       if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
+               if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
+                       DMA_OUT(chan, &chan->regs->bcr, 0, 32);
+                       mode |= FSL_DMA_MR_EMP_EN;
+               } else {
+                       mode &= ~FSL_DMA_MR_EMP_EN;
+               }
        }
 
-       if (fsl_chan->feature & FSL_DMA_CHAN_START_EXT)
-               mr_set |= FSL_DMA_MR_EMS_EN;
+       if (chan->feature & FSL_DMA_CHAN_START_EXT)
+               mode |= FSL_DMA_MR_EMS_EN;
        else
-               mr_set |= FSL_DMA_MR_CS;
+               mode |= FSL_DMA_MR_CS;
 
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-                       DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
-                       | mr_set, 32);
+       DMA_OUT(chan, &chan->regs->mr, mode, 32);
 }
 
-static void dma_halt(struct fsl_dma_chan *fsl_chan)
+static void dma_halt(struct fsldma_chan *chan)
 {
+       u32 mode;
        int i;
 
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-               DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) | FSL_DMA_MR_CA,
-               32);
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-               DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) & ~(FSL_DMA_MR_CS
-               | FSL_DMA_MR_EMS_EN | FSL_DMA_MR_CA), 32);
+       mode = DMA_IN(chan, &chan->regs->mr, 32);
+       mode |= FSL_DMA_MR_CA;
+       DMA_OUT(chan, &chan->regs->mr, mode, 32);
+
+       mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN | FSL_DMA_MR_CA);
+       DMA_OUT(chan, &chan->regs->mr, mode, 32);
 
        for (i = 0; i < 100; i++) {
-               if (dma_is_idle(fsl_chan))
-                       break;
+               if (dma_is_idle(chan))
+                       return;
+
                udelay(10);
        }
-       if (i >= 100 && !dma_is_idle(fsl_chan))
-               dev_err(fsl_chan->dev, "DMA halt timeout!\n");
+
+       if (!dma_is_idle(chan))
+               dev_err(chan->dev, "DMA halt timeout!\n");
 }
 
-static void set_ld_eol(struct fsl_dma_chan *fsl_chan,
+static void set_ld_eol(struct fsldma_chan *chan,
                        struct fsl_desc_sw *desc)
 {
        u64 snoop_bits;
 
-       snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
+       snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
                ? FSL_DMA_SNEN : 0;
 
-       desc->hw.next_ln_addr = CPU_TO_DMA(fsl_chan,
-               DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL
+       desc->hw.next_ln_addr = CPU_TO_DMA(chan,
+               DMA_TO_CPU(chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL
                        | snoop_bits, 64);
 }
 
-static void append_ld_queue(struct fsl_dma_chan *fsl_chan,
-               struct fsl_desc_sw *new_desc)
-{
-       struct fsl_desc_sw *queue_tail = to_fsl_desc(fsl_chan->ld_queue.prev);
-
-       if (list_empty(&fsl_chan->ld_queue))
-               return;
-
-       /* Link to the new descriptor physical address and
-        * Enable End-of-segment interrupt for
-        * the last link descriptor.
-        * (the previous node's next link descriptor)
-        *
-        * For FSL_DMA_IP_83xx, the snoop enable bit need be set.
-        */
-       queue_tail->hw.next_ln_addr = CPU_TO_DMA(fsl_chan,
-                       new_desc->async_tx.phys | FSL_DMA_EOSIE |
-                       (((fsl_chan->feature & FSL_DMA_IP_MASK)
-                               == FSL_DMA_IP_83XX) ? FSL_DMA_SNEN : 0), 64);
-}
-
 /**
  * fsl_chan_set_src_loop_size - Set source address hold transfer size
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  * @size     : Address loop size, 0 for disable loop
  *
  * The set source address hold transfer size. The source
@@ -229,29 +205,30 @@ static void append_ld_queue(struct fsl_dma_chan *fsl_chan,
  * read data from SA, SA + 1, SA + 2, SA + 3, then loop back to SA,
  * SA + 1 ... and so on.
  */
-static void fsl_chan_set_src_loop_size(struct fsl_dma_chan *fsl_chan, int size)
+static void fsl_chan_set_src_loop_size(struct fsldma_chan *chan, int size)
 {
+       u32 mode;
+
+       mode = DMA_IN(chan, &chan->regs->mr, 32);
+
        switch (size) {
        case 0:
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-                       DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) &
-                       (~FSL_DMA_MR_SAHE), 32);
+               mode &= ~FSL_DMA_MR_SAHE;
                break;
        case 1:
        case 2:
        case 4:
        case 8:
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-                       DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) |
-                       FSL_DMA_MR_SAHE | (__ilog2(size) << 14),
-                       32);
+               mode |= FSL_DMA_MR_SAHE | (__ilog2(size) << 14);
                break;
        }
+
+       DMA_OUT(chan, &chan->regs->mr, mode, 32);
 }
 
 /**
- * fsl_chan_set_dest_loop_size - Set destination address hold transfer size
- * @fsl_chan : Freescale DMA channel
+ * fsl_chan_set_dst_loop_size - Set destination address hold transfer size
+ * @chan : Freescale DMA channel
  * @size     : Address loop size, 0 for disable loop
  *
  * The set destination address hold transfer size. The destination
@@ -260,29 +237,30 @@ static void fsl_chan_set_src_loop_size(struct fsl_dma_chan *fsl_chan, int size)
  * write data to TA, TA + 1, TA + 2, TA + 3, then loop back to TA,
  * TA + 1 ... and so on.
  */
-static void fsl_chan_set_dest_loop_size(struct fsl_dma_chan *fsl_chan, int size)
+static void fsl_chan_set_dst_loop_size(struct fsldma_chan *chan, int size)
 {
+       u32 mode;
+
+       mode = DMA_IN(chan, &chan->regs->mr, 32);
+
        switch (size) {
        case 0:
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-                       DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) &
-                       (~FSL_DMA_MR_DAHE), 32);
+               mode &= ~FSL_DMA_MR_DAHE;
                break;
        case 1:
        case 2:
        case 4:
        case 8:
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-                       DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) |
-                       FSL_DMA_MR_DAHE | (__ilog2(size) << 16),
-                       32);
+               mode |= FSL_DMA_MR_DAHE | (__ilog2(size) << 16);
                break;
        }
+
+       DMA_OUT(chan, &chan->regs->mr, mode, 32);
 }
 
 /**
  * fsl_chan_set_request_count - Set DMA Request Count for external control
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  * @size     : Number of bytes to transfer in a single request
  *
  * The Freescale DMA channel can be controlled by the external signal DREQ#.
@@ -292,35 +270,38 @@ static void fsl_chan_set_dest_loop_size(struct fsl_dma_chan *fsl_chan, int size)
  *
  * A size of 0 disables external pause control. The maximum size is 1024.
  */
-static void fsl_chan_set_request_count(struct fsl_dma_chan *fsl_chan, int size)
+static void fsl_chan_set_request_count(struct fsldma_chan *chan, int size)
 {
+       u32 mode;
+
        BUG_ON(size > 1024);
-       DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
-               DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
-                       | ((__ilog2(size) << 24) & 0x0f000000),
-               32);
+
+       mode = DMA_IN(chan, &chan->regs->mr, 32);
+       mode |= (__ilog2(size) << 24) & 0x0f000000;
+
+       DMA_OUT(chan, &chan->regs->mr, mode, 32);
 }
 
 /**
  * fsl_chan_toggle_ext_pause - Toggle channel external pause status
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  * @enable   : 0 is disabled, 1 is enabled.
  *
  * The Freescale DMA channel can be controlled by the external signal DREQ#.
  * The DMA Request Count feature should be used in addition to this feature
  * to set the number of bytes to transfer before pausing the channel.
  */
-static void fsl_chan_toggle_ext_pause(struct fsl_dma_chan *fsl_chan, int enable)
+static void fsl_chan_toggle_ext_pause(struct fsldma_chan *chan, int enable)
 {
        if (enable)
-               fsl_chan->feature |= FSL_DMA_CHAN_PAUSE_EXT;
+               chan->feature |= FSL_DMA_CHAN_PAUSE_EXT;
        else
-               fsl_chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT;
+               chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT;
 }
 
 /**
  * fsl_chan_toggle_ext_start - Toggle channel external start status
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  * @enable   : 0 is disabled, 1 is enabled.
  *
  * If enable the external start, the channel can be started by an
@@ -328,141 +309,196 @@ static void fsl_chan_toggle_ext_pause(struct fsl_dma_chan *fsl_chan, int enable)
  * transfer immediately. The DMA channel will wait for the
  * control pin asserted.
  */
-static void fsl_chan_toggle_ext_start(struct fsl_dma_chan *fsl_chan, int enable)
+static void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable)
 {
        if (enable)
-               fsl_chan->feature |= FSL_DMA_CHAN_START_EXT;
+               chan->feature |= FSL_DMA_CHAN_START_EXT;
        else
-               fsl_chan->feature &= ~FSL_DMA_CHAN_START_EXT;
+               chan->feature &= ~FSL_DMA_CHAN_START_EXT;
+}
+
+static void append_ld_queue(struct fsldma_chan *chan,
+                           struct fsl_desc_sw *desc)
+{
+       struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev);
+
+       if (list_empty(&chan->ld_pending))
+               goto out_splice;
+
+       /*
+        * Add the hardware descriptor to the chain of hardware descriptors
+        * that already exists in memory.
+        *
+        * This will un-set the EOL bit of the existing transaction, and the
+        * last link in this transaction will become the EOL descriptor.
+        */
+       set_desc_next(chan, &tail->hw, desc->async_tx.phys);
+
+       /*
+        * Add the software descriptor and all children to the list
+        * of pending transactions
+        */
+out_splice:
+       list_splice_tail_init(&desc->tx_list, &chan->ld_pending);
 }
 
 static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
 {
-       struct fsl_dma_chan *fsl_chan = to_fsl_chan(tx->chan);
+       struct fsldma_chan *chan = to_fsl_chan(tx->chan);
        struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
        struct fsl_desc_sw *child;
        unsigned long flags;
        dma_cookie_t cookie;
 
-       /* cookie increment and adding to ld_queue must be atomic */
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+       spin_lock_irqsave(&chan->desc_lock, flags);
 
-       cookie = fsl_chan->common.cookie;
+       /*
+        * assign cookies to all of the software descriptors
+        * that make up this transaction
+        */
+       cookie = chan->common.cookie;
        list_for_each_entry(child, &desc->tx_list, node) {
                cookie++;
                if (cookie < 0)
                        cookie = 1;
 
-               desc->async_tx.cookie = cookie;
+               child->async_tx.cookie = cookie;
        }
 
-       fsl_chan->common.cookie = cookie;
-       append_ld_queue(fsl_chan, desc);
-       list_splice_init(&desc->tx_list, fsl_chan->ld_queue.prev);
+       chan->common.cookie = cookie;
+
+       /* put this transaction onto the tail of the pending queue */
+       append_ld_queue(chan, desc);
 
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
+       spin_unlock_irqrestore(&chan->desc_lock, flags);
 
        return cookie;
 }
 
 /**
  * fsl_dma_alloc_descriptor - Allocate descriptor from channel's DMA pool.
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  *
  * Return - The descriptor allocated. NULL for failed.
  */
 static struct fsl_desc_sw *fsl_dma_alloc_descriptor(
-                                       struct fsl_dma_chan *fsl_chan)
+                                       struct fsldma_chan *chan)
 {
+       struct fsl_desc_sw *desc;
        dma_addr_t pdesc;
-       struct fsl_desc_sw *desc_sw;
-
-       desc_sw = dma_pool_alloc(fsl_chan->desc_pool, GFP_ATOMIC, &pdesc);
-       if (desc_sw) {
-               memset(desc_sw, 0, sizeof(struct fsl_desc_sw));
-               INIT_LIST_HEAD(&desc_sw->tx_list);
-               dma_async_tx_descriptor_init(&desc_sw->async_tx,
-                                               &fsl_chan->common);
-               desc_sw->async_tx.tx_submit = fsl_dma_tx_submit;
-               desc_sw->async_tx.phys = pdesc;
+
+       desc = dma_pool_alloc(chan->desc_pool, GFP_ATOMIC, &pdesc);
+       if (!desc) {
+               dev_dbg(chan->dev, "out of memory for link desc\n");
+               return NULL;
        }
 
-       return desc_sw;
+       memset(desc, 0, sizeof(*desc));
+       INIT_LIST_HEAD(&desc->tx_list);
+       dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
+       desc->async_tx.tx_submit = fsl_dma_tx_submit;
+       desc->async_tx.phys = pdesc;
+
+       return desc;
 }
 
 
 /**
  * fsl_dma_alloc_chan_resources - Allocate resources for DMA channel.
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  *
  * This function will create a dma pool for descriptor allocation.
  *
  * Return - The number of descriptors allocated.
  */
-static int fsl_dma_alloc_chan_resources(struct dma_chan *chan)
+static int fsl_dma_alloc_chan_resources(struct dma_chan *dchan)
 {
-       struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
+       struct fsldma_chan *chan = to_fsl_chan(dchan);
 
        /* Has this channel already been allocated? */
-       if (fsl_chan->desc_pool)
+       if (chan->desc_pool)
                return 1;
 
-       /* We need the descriptor to be aligned to 32bytes
+       /*
+        * We need the descriptor to be aligned to 32bytes
         * for meeting FSL DMA specification requirement.
         */
-       fsl_chan->desc_pool = dma_pool_create("fsl_dma_engine_desc_pool",
-                       fsl_chan->dev, sizeof(struct fsl_desc_sw),
-                       32, 0);
-       if (!fsl_chan->desc_pool) {
-               dev_err(fsl_chan->dev, "No memory for channel %d "
-                       "descriptor dma pool.\n", fsl_chan->id);
-               return 0;
+       chan->desc_pool = dma_pool_create("fsl_dma_engine_desc_pool",
+                                         chan->dev,
+                                         sizeof(struct fsl_desc_sw),
+                                         __alignof__(struct fsl_desc_sw), 0);
+       if (!chan->desc_pool) {
+               dev_err(chan->dev, "unable to allocate channel %d "
+                                  "descriptor pool\n", chan->id);
+               return -ENOMEM;
        }
 
+       /* there is at least one descriptor free to be allocated */
        return 1;
 }
 
 /**
- * fsl_dma_free_chan_resources - Free all resources of the channel.
- * @fsl_chan : Freescale DMA channel
+ * fsldma_free_desc_list - Free all descriptors in a queue
+ * @chan: Freescae DMA channel
+ * @list: the list to free
+ *
+ * LOCKING: must hold chan->desc_lock
  */
-static void fsl_dma_free_chan_resources(struct dma_chan *chan)
+static void fsldma_free_desc_list(struct fsldma_chan *chan,
+                                 struct list_head *list)
 {
-       struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
        struct fsl_desc_sw *desc, *_desc;
-       unsigned long flags;
 
-       dev_dbg(fsl_chan->dev, "Free all channel resources.\n");
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
-       list_for_each_entry_safe(desc, _desc, &fsl_chan->ld_queue, node) {
-#ifdef FSL_DMA_LD_DEBUG
-               dev_dbg(fsl_chan->dev,
-                               "LD %p will be released.\n", desc);
-#endif
+       list_for_each_entry_safe(desc, _desc, list, node) {
+               list_del(&desc->node);
+               dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
+       }
+}
+
+static void fsldma_free_desc_list_reverse(struct fsldma_chan *chan,
+                                         struct list_head *list)
+{
+       struct fsl_desc_sw *desc, *_desc;
+
+       list_for_each_entry_safe_reverse(desc, _desc, list, node) {
                list_del(&desc->node);
-               /* free link descriptor */
-               dma_pool_free(fsl_chan->desc_pool, desc, desc->async_tx.phys);
+               dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
        }
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
-       dma_pool_destroy(fsl_chan->desc_pool);
+}
+
+/**
+ * fsl_dma_free_chan_resources - Free all resources of the channel.
+ * @chan : Freescale DMA channel
+ */
+static void fsl_dma_free_chan_resources(struct dma_chan *dchan)
+{
+       struct fsldma_chan *chan = to_fsl_chan(dchan);
+       unsigned long flags;
+
+       dev_dbg(chan->dev, "Free all channel resources.\n");
+       spin_lock_irqsave(&chan->desc_lock, flags);
+       fsldma_free_desc_list(chan, &chan->ld_pending);
+       fsldma_free_desc_list(chan, &chan->ld_running);
+       spin_unlock_irqrestore(&chan->desc_lock, flags);
 
-       fsl_chan->desc_pool = NULL;
+       dma_pool_destroy(chan->desc_pool);
+       chan->desc_pool = NULL;
 }
 
 static struct dma_async_tx_descriptor *
-fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags)
+fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags)
 {
-       struct fsl_dma_chan *fsl_chan;
+       struct fsldma_chan *chan;
        struct fsl_desc_sw *new;
 
-       if (!chan)
+       if (!dchan)
                return NULL;
 
-       fsl_chan = to_fsl_chan(chan);
+       chan = to_fsl_chan(dchan);
 
-       new = fsl_dma_alloc_descriptor(fsl_chan);
+       new = fsl_dma_alloc_descriptor(chan);
        if (!new) {
-               dev_err(fsl_chan->dev, "No free memory for link descriptor\n");
+               dev_err(chan->dev, "No free memory for link descriptor\n");
                return NULL;
        }
 
@@ -473,51 +509,50 @@ fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags)
        list_add_tail(&new->node, &new->tx_list);
 
        /* Set End-of-link to the last link descriptor of new list*/
-       set_ld_eol(fsl_chan, new);
+       set_ld_eol(chan, new);
 
        return &new->async_tx;
 }
 
 static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
-       struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
+       struct dma_chan *dchan, dma_addr_t dma_dst, dma_addr_t dma_src,
        size_t len, unsigned long flags)
 {
-       struct fsl_dma_chan *fsl_chan;
+       struct fsldma_chan *chan;
        struct fsl_desc_sw *first = NULL, *prev = NULL, *new;
-       struct list_head *list;
        size_t copy;
 
-       if (!chan)
+       if (!dchan)
                return NULL;
 
        if (!len)
                return NULL;
 
-       fsl_chan = to_fsl_chan(chan);
+       chan = to_fsl_chan(dchan);
 
        do {
 
                /* Allocate the link descriptor from DMA pool */
-               new = fsl_dma_alloc_descriptor(fsl_chan);
+               new = fsl_dma_alloc_descriptor(chan);
                if (!new) {
-                       dev_err(fsl_chan->dev,
+                       dev_err(chan->dev,
                                        "No free memory for link descriptor\n");
                        goto fail;
                }
 #ifdef FSL_DMA_LD_DEBUG
-               dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new);
+               dev_dbg(chan->dev, "new link desc alloc %p\n", new);
 #endif
 
                copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT);
 
-               set_desc_cnt(fsl_chan, &new->hw, copy);
-               set_desc_src(fsl_chan, &new->hw, dma_src);
-               set_desc_dest(fsl_chan, &new->hw, dma_dest);
+               set_desc_cnt(chan, &new->hw, copy);
+               set_desc_src(chan, &new->hw, dma_src);
+               set_desc_dst(chan, &new->hw, dma_dst);
 
                if (!first)
                        first = new;
                else
-                       set_desc_next(fsl_chan, &prev->hw, new->async_tx.phys);
+                       set_desc_next(chan, &prev->hw, new->async_tx.phys);
 
                new->async_tx.cookie = 0;
                async_tx_ack(&new->async_tx);
@@ -525,7 +560,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
                prev = new;
                len -= copy;
                dma_src += copy;
-               dma_dest += copy;
+               dma_dst += copy;
 
                /* Insert the link descriptor to the LD ring */
                list_add_tail(&new->node, &first->tx_list);
@@ -535,7 +570,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
        new->async_tx.cookie = -EBUSY;
 
        /* Set End-of-link to the last link descriptor of new list*/
-       set_ld_eol(fsl_chan, new);
+       set_ld_eol(chan, new);
 
        return &first->async_tx;
 
@@ -543,12 +578,7 @@ fail:
        if (!first)
                return NULL;
 
-       list = &first->tx_list;
-       list_for_each_entry_safe_reverse(new, prev, list, node) {
-               list_del(&new->node);
-               dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys);
-       }
-
+       fsldma_free_desc_list_reverse(chan, &first->tx_list);
        return NULL;
 }
 
@@ -565,13 +595,12 @@ fail:
  * chan->private variable.
  */
 static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
-       struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
+       struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len,
        enum dma_data_direction direction, unsigned long flags)
 {
-       struct fsl_dma_chan *fsl_chan;
+       struct fsldma_chan *chan;
        struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
        struct fsl_dma_slave *slave;
-       struct list_head *tx_list;
        size_t copy;
 
        int i;
@@ -581,14 +610,14 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
        struct fsl_dma_hw_addr *hw;
        dma_addr_t dma_dst, dma_src;
 
-       if (!chan)
+       if (!dchan)
                return NULL;
 
-       if (!chan->private)
+       if (!dchan->private)
                return NULL;
 
-       fsl_chan = to_fsl_chan(chan);
-       slave = chan->private;
+       chan = to_fsl_chan(dchan);
+       slave = dchan->private;
 
        if (list_empty(&slave->addresses))
                return NULL;
@@ -637,14 +666,14 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
                        }
 
                        /* Allocate the link descriptor from DMA pool */
-                       new = fsl_dma_alloc_descriptor(fsl_chan);
+                       new = fsl_dma_alloc_descriptor(chan);
                        if (!new) {
-                               dev_err(fsl_chan->dev, "No free memory for "
+                               dev_err(chan->dev, "No free memory for "
                                                       "link descriptor\n");
                                goto fail;
                        }
 #ifdef FSL_DMA_LD_DEBUG
-                       dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new);
+                       dev_dbg(chan->dev, "new link desc alloc %p\n", new);
 #endif
 
                        /*
@@ -671,9 +700,9 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
                        }
 
                        /* Fill in the descriptor */
-                       set_desc_cnt(fsl_chan, &new->hw, copy);
-                       set_desc_src(fsl_chan, &new->hw, dma_src);
-                       set_desc_dest(fsl_chan, &new->hw, dma_dst);
+                       set_desc_cnt(chan, &new->hw, copy);
+                       set_desc_src(chan, &new->hw, dma_src);
+                       set_desc_dst(chan, &new->hw, dma_dst);
 
                        /*
                         * If this is not the first descriptor, chain the
@@ -682,7 +711,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_slave_sg(
                        if (!first) {
                                first = new;
                        } else {
-                               set_desc_next(fsl_chan, &prev->hw,
+                               set_desc_next(chan, &prev->hw,
                                              new->async_tx.phys);
                        }
 
@@ -708,23 +737,23 @@ finished:
        new->async_tx.cookie = -EBUSY;
 
        /* Set End-of-link to the last link descriptor of new list */
-       set_ld_eol(fsl_chan, new);
+       set_ld_eol(chan, new);
 
        /* Enable extra controller features */
-       if (fsl_chan->set_src_loop_size)
-               fsl_chan->set_src_loop_size(fsl_chan, slave->src_loop_size);
+       if (chan->set_src_loop_size)
+               chan->set_src_loop_size(chan, slave->src_loop_size);
 
-       if (fsl_chan->set_dest_loop_size)
-               fsl_chan->set_dest_loop_size(fsl_chan, slave->dst_loop_size);
+       if (chan->set_dst_loop_size)
+               chan->set_dst_loop_size(chan, slave->dst_loop_size);
 
-       if (fsl_chan->toggle_ext_start)
-               fsl_chan->toggle_ext_start(fsl_chan, slave->external_start);
+       if (chan->toggle_ext_start)
+               chan->toggle_ext_start(chan, slave->external_start);
 
-       if (fsl_chan->toggle_ext_pause)
-               fsl_chan->toggle_ext_pause(fsl_chan, slave->external_pause);
+       if (chan->toggle_ext_pause)
+               chan->toggle_ext_pause(chan, slave->external_pause);
 
-       if (fsl_chan->set_request_count)
-               fsl_chan->set_request_count(fsl_chan, slave->request_count);
+       if (chan->set_request_count)
+               chan->set_request_count(chan, slave->request_count);
 
        return &first->async_tx;
 
@@ -741,215 +770,216 @@ fail:
         *
         * We're re-using variables for the loop, oh well
         */
-       tx_list = &first->tx_list;
-       list_for_each_entry_safe_reverse(new, prev, tx_list, node) {
-               list_del_init(&new->node);
-               dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys);
-       }
-
+       fsldma_free_desc_list_reverse(chan, &first->tx_list);
        return NULL;
 }
 
-static void fsl_dma_device_terminate_all(struct dma_chan *chan)
+static void fsl_dma_device_terminate_all(struct dma_chan *dchan)
 {
-       struct fsl_dma_chan *fsl_chan;
-       struct fsl_desc_sw *desc, *tmp;
+       struct fsldma_chan *chan;
        unsigned long flags;
 
-       if (!chan)
+       if (!dchan)
                return;
 
-       fsl_chan = to_fsl_chan(chan);
+       chan = to_fsl_chan(dchan);
 
        /* Halt the DMA engine */
-       dma_halt(fsl_chan);
+       dma_halt(chan);
 
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+       spin_lock_irqsave(&chan->desc_lock, flags);
 
        /* Remove and free all of the descriptors in the LD queue */
-       list_for_each_entry_safe(desc, tmp, &fsl_chan->ld_queue, node) {
-               list_del(&desc->node);
-               dma_pool_free(fsl_chan->desc_pool, desc, desc->async_tx.phys);
-       }
+       fsldma_free_desc_list(chan, &chan->ld_pending);
+       fsldma_free_desc_list(chan, &chan->ld_running);
 
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
+       spin_unlock_irqrestore(&chan->desc_lock, flags);
 }
 
 /**
  * fsl_dma_update_completed_cookie - Update the completed cookie.
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
+ *
+ * CONTEXT: hardirq
  */
-static void fsl_dma_update_completed_cookie(struct fsl_dma_chan *fsl_chan)
+static void fsl_dma_update_completed_cookie(struct fsldma_chan *chan)
 {
-       struct fsl_desc_sw *cur_desc, *desc;
-       dma_addr_t ld_phy;
+       struct fsl_desc_sw *desc;
+       unsigned long flags;
+       dma_cookie_t cookie;
 
-       ld_phy = get_cdar(fsl_chan) & FSL_DMA_NLDA_MASK;
+       spin_lock_irqsave(&chan->desc_lock, flags);
 
-       if (ld_phy) {
-               cur_desc = NULL;
-               list_for_each_entry(desc, &fsl_chan->ld_queue, node)
-                       if (desc->async_tx.phys == ld_phy) {
-                               cur_desc = desc;
-                               break;
-                       }
+       if (list_empty(&chan->ld_running)) {
+               dev_dbg(chan->dev, "no running descriptors\n");
+               goto out_unlock;
+       }
 
-               if (cur_desc && cur_desc->async_tx.cookie) {
-                       if (dma_is_idle(fsl_chan))
-                               fsl_chan->completed_cookie =
-                                       cur_desc->async_tx.cookie;
-                       else
-                               fsl_chan->completed_cookie =
-                                       cur_desc->async_tx.cookie - 1;
-               }
+       /* Get the last descriptor, update the cookie to that */
+       desc = to_fsl_desc(chan->ld_running.prev);
+       if (dma_is_idle(chan))
+               cookie = desc->async_tx.cookie;
+       else {
+               cookie = desc->async_tx.cookie - 1;
+               if (unlikely(cookie < DMA_MIN_COOKIE))
+                       cookie = DMA_MAX_COOKIE;
        }
+
+       chan->completed_cookie = cookie;
+
+out_unlock:
+       spin_unlock_irqrestore(&chan->desc_lock, flags);
+}
+
+/**
+ * fsldma_desc_status - Check the status of a descriptor
+ * @chan: Freescale DMA channel
+ * @desc: DMA SW descriptor
+ *
+ * This function will return the status of the given descriptor
+ */
+static enum dma_status fsldma_desc_status(struct fsldma_chan *chan,
+                                         struct fsl_desc_sw *desc)
+{
+       return dma_async_is_complete(desc->async_tx.cookie,
+                                    chan->completed_cookie,
+                                    chan->common.cookie);
 }
 
 /**
  * fsl_chan_ld_cleanup - Clean up link descriptors
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  *
  * This function clean up the ld_queue of DMA channel.
- * If 'in_intr' is set, the function will move the link descriptor to
- * the recycle list. Otherwise, free it directly.
  */
-static void fsl_chan_ld_cleanup(struct fsl_dma_chan *fsl_chan)
+static void fsl_chan_ld_cleanup(struct fsldma_chan *chan)
 {
        struct fsl_desc_sw *desc, *_desc;
        unsigned long flags;
 
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+       spin_lock_irqsave(&chan->desc_lock, flags);
 
-       dev_dbg(fsl_chan->dev, "chan completed_cookie = %d\n",
-                       fsl_chan->completed_cookie);
-       list_for_each_entry_safe(desc, _desc, &fsl_chan->ld_queue, node) {
+       dev_dbg(chan->dev, "chan completed_cookie = %d\n", chan->completed_cookie);
+       list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) {
                dma_async_tx_callback callback;
                void *callback_param;
 
-               if (dma_async_is_complete(desc->async_tx.cookie,
-                           fsl_chan->completed_cookie, fsl_chan->common.cookie)
-                               == DMA_IN_PROGRESS)
+               if (fsldma_desc_status(chan, desc) == DMA_IN_PROGRESS)
                        break;
 
-               callback = desc->async_tx.callback;
-               callback_param = desc->async_tx.callback_param;
-
-               /* Remove from ld_queue list */
+               /* Remove from the list of running transactions */
                list_del(&desc->node);
 
-               dev_dbg(fsl_chan->dev, "link descriptor %p will be recycle.\n",
-                               desc);
-               dma_pool_free(fsl_chan->desc_pool, desc, desc->async_tx.phys);
-
                /* Run the link descriptor callback function */
+               callback = desc->async_tx.callback;
+               callback_param = desc->async_tx.callback_param;
                if (callback) {
-                       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
-                       dev_dbg(fsl_chan->dev, "link descriptor %p callback\n",
-                                       desc);
+                       spin_unlock_irqrestore(&chan->desc_lock, flags);
+                       dev_dbg(chan->dev, "LD %p callback\n", desc);
                        callback(callback_param);
-                       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+                       spin_lock_irqsave(&chan->desc_lock, flags);
                }
+
+               /* Run any dependencies, then free the descriptor */
+               dma_run_dependencies(&desc->async_tx);
+               dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
        }
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
+
+       spin_unlock_irqrestore(&chan->desc_lock, flags);
 }
 
 /**
- * fsl_chan_xfer_ld_queue - Transfer link descriptors in channel ld_queue.
- * @fsl_chan : Freescale DMA channel
+ * fsl_chan_xfer_ld_queue - transfer any pending transactions
+ * @chan : Freescale DMA channel
+ *
+ * This will make sure that any pending transactions will be run.
+ * If the DMA controller is idle, it will be started. Otherwise,
+ * the DMA controller's interrupt handler will start any pending
+ * transactions when it becomes idle.
  */
-static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan)
+static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan)
 {
-       struct list_head *ld_node;
-       dma_addr_t next_dest_addr;
+       struct fsl_desc_sw *desc;
        unsigned long flags;
 
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+       spin_lock_irqsave(&chan->desc_lock, flags);
 
-       if (!dma_is_idle(fsl_chan))
+       /*
+        * If the list of pending descriptors is empty, then we
+        * don't need to do any work at all
+        */
+       if (list_empty(&chan->ld_pending)) {
+               dev_dbg(chan->dev, "no pending LDs\n");
                goto out_unlock;
+       }
 
-       dma_halt(fsl_chan);
+       /*
+        * The DMA controller is not idle, which means the interrupt
+        * handler will start any queued transactions when it runs
+        * at the end of the current transaction
+        */
+       if (!dma_is_idle(chan)) {
+               dev_dbg(chan->dev, "DMA controller still busy\n");
+               goto out_unlock;
+       }
 
-       /* If there are some link descriptors
-        * not transfered in queue. We need to start it.
+       /*
+        * TODO:
+        * make sure the dma_halt() function really un-wedges the
+        * controller as much as possible
         */
+       dma_halt(chan);
 
-       /* Find the first un-transfer desciptor */
-       for (ld_node = fsl_chan->ld_queue.next;
-               (ld_node != &fsl_chan->ld_queue)
-                       && (dma_async_is_complete(
-                               to_fsl_desc(ld_node)->async_tx.cookie,
-                               fsl_chan->completed_cookie,
-                               fsl_chan->common.cookie) == DMA_SUCCESS);
-               ld_node = ld_node->next);
-
-       if (ld_node != &fsl_chan->ld_queue) {
-               /* Get the ld start address from ld_queue */
-               next_dest_addr = to_fsl_desc(ld_node)->async_tx.phys;
-               dev_dbg(fsl_chan->dev, "xfer LDs staring from 0x%llx\n",
-                               (unsigned long long)next_dest_addr);
-               set_cdar(fsl_chan, next_dest_addr);
-               dma_start(fsl_chan);
-       } else {
-               set_cdar(fsl_chan, 0);
-               set_ndar(fsl_chan, 0);
-       }
+       /*
+        * If there are some link descriptors which have not been
+        * transferred, we need to start the controller
+        */
+
+       /*
+        * Move all elements from the queue of pending transactions
+        * onto the list of running transactions
+        */
+       desc = list_first_entry(&chan->ld_pending, struct fsl_desc_sw, node);
+       list_splice_tail_init(&chan->ld_pending, &chan->ld_running);
+
+       /*
+        * Program the descriptor's address into the DMA controller,
+        * then start the DMA transaction
+        */
+       set_cdar(chan, desc->async_tx.phys);
+       dma_start(chan);
 
 out_unlock:
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
+       spin_unlock_irqrestore(&chan->desc_lock, flags);
 }
 
 /**
  * fsl_dma_memcpy_issue_pending - Issue the DMA start command
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  */
-static void fsl_dma_memcpy_issue_pending(struct dma_chan *chan)
+static void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan)
 {
-       struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
-
-#ifdef FSL_DMA_LD_DEBUG
-       struct fsl_desc_sw *ld;
-       unsigned long flags;
-
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
-       if (list_empty(&fsl_chan->ld_queue)) {
-               spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
-               return;
-       }
-
-       dev_dbg(fsl_chan->dev, "--memcpy issue--\n");
-       list_for_each_entry(ld, &fsl_chan->ld_queue, node) {
-               int i;
-               dev_dbg(fsl_chan->dev, "Ch %d, LD %08x\n",
-                               fsl_chan->id, ld->async_tx.phys);
-               for (i = 0; i < 8; i++)
-                       dev_dbg(fsl_chan->dev, "LD offset %d: %08x\n",
-                                       i, *(((u32 *)&ld->hw) + i));
-       }
-       dev_dbg(fsl_chan->dev, "----------------\n");
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
-#endif
-
-       fsl_chan_xfer_ld_queue(fsl_chan);
+       struct fsldma_chan *chan = to_fsl_chan(dchan);
+       fsl_chan_xfer_ld_queue(chan);
 }
 
 /**
  * fsl_dma_is_complete - Determine the DMA status
- * @fsl_chan : Freescale DMA channel
+ * @chan : Freescale DMA channel
  */
-static enum dma_status fsl_dma_is_complete(struct dma_chan *chan,
+static enum dma_status fsl_dma_is_complete(struct dma_chan *dchan,
                                        dma_cookie_t cookie,
                                        dma_cookie_t *done,
                                        dma_cookie_t *used)
 {
-       struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
+       struct fsldma_chan *chan = to_fsl_chan(dchan);
        dma_cookie_t last_used;
        dma_cookie_t last_complete;
 
-       fsl_chan_ld_cleanup(fsl_chan);
+       fsl_chan_ld_cleanup(chan);
 
-       last_used = chan->cookie;
-       last_complete = fsl_chan->completed_cookie;
+       last_used = dchan->cookie;
+       last_complete = chan->completed_cookie;
 
        if (done)
                *done = last_complete;
@@ -960,32 +990,37 @@ static enum dma_status fsl_dma_is_complete(struct dma_chan *chan,
        return dma_async_is_complete(cookie, last_complete, last_used);
 }
 
-static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data)
+/*----------------------------------------------------------------------------*/
+/* Interrupt Handling                                                         */
+/*----------------------------------------------------------------------------*/
+
+static irqreturn_t fsldma_chan_irq(int irq, void *data)
 {
-       struct fsl_dma_chan *fsl_chan = (struct fsl_dma_chan *)data;
-       u32 stat;
+       struct fsldma_chan *chan = data;
        int update_cookie = 0;
        int xfer_ld_q = 0;
+       u32 stat;
 
-       stat = get_sr(fsl_chan);
-       dev_dbg(fsl_chan->dev, "event: channel %d, stat = 0x%x\n",
-                                               fsl_chan->id, stat);
-       set_sr(fsl_chan, stat);         /* Clear the event register */
+       /* save and clear the status register */
+       stat = get_sr(chan);
+       set_sr(chan, stat);
+       dev_dbg(chan->dev, "irq: channel %d, stat = 0x%x\n", chan->id, stat);
 
        stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH);
        if (!stat)
                return IRQ_NONE;
 
        if (stat & FSL_DMA_SR_TE)
-               dev_err(fsl_chan->dev, "Transfer Error!\n");
+               dev_err(chan->dev, "Transfer Error!\n");
 
-       /* Programming Error
+       /*
+        * Programming Error
         * The DMA_INTERRUPT async_tx is a NULL transfer, which will
         * triger a PE interrupt.
         */
        if (stat & FSL_DMA_SR_PE) {
-               dev_dbg(fsl_chan->dev, "event: Programming Error INT\n");
-               if (get_bcr(fsl_chan) == 0) {
+               dev_dbg(chan->dev, "irq: Programming Error INT\n");
+               if (get_bcr(chan) == 0) {
                        /* BCR register is 0, this is a DMA_INTERRUPT async_tx.
                         * Now, update the completed cookie, and continue the
                         * next uncompleted transfer.
@@ -996,208 +1031,296 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data)
                stat &= ~FSL_DMA_SR_PE;
        }
 
-       /* If the link descriptor segment transfer finishes,
+       /*
+        * If the link descriptor segment transfer finishes,
         * we will recycle the used descriptor.
         */
        if (stat & FSL_DMA_SR_EOSI) {
-               dev_dbg(fsl_chan->dev, "event: End-of-segments INT\n");
-               dev_dbg(fsl_chan->dev, "event: clndar 0x%llx, nlndar 0x%llx\n",
-                       (unsigned long long)get_cdar(fsl_chan),
-                       (unsigned long long)get_ndar(fsl_chan));
+               dev_dbg(chan->dev, "irq: End-of-segments INT\n");
+               dev_dbg(chan->dev, "irq: clndar 0x%llx, nlndar 0x%llx\n",
+                       (unsigned long long)get_cdar(chan),
+                       (unsigned long long)get_ndar(chan));
                stat &= ~FSL_DMA_SR_EOSI;
                update_cookie = 1;
        }
 
-       /* For MPC8349, EOCDI event need to update cookie
+       /*
+        * For MPC8349, EOCDI event need to update cookie
         * and start the next transfer if it exist.
         */
        if (stat & FSL_DMA_SR_EOCDI) {
-               dev_dbg(fsl_chan->dev, "event: End-of-Chain link INT\n");
+               dev_dbg(chan->dev, "irq: End-of-Chain link INT\n");
                stat &= ~FSL_DMA_SR_EOCDI;
                update_cookie = 1;
                xfer_ld_q = 1;
        }
 
-       /* If it current transfer is the end-of-transfer,
+       /*
+        * If it current transfer is the end-of-transfer,
         * we should clear the Channel Start bit for
         * prepare next transfer.
         */
        if (stat & FSL_DMA_SR_EOLNI) {
-               dev_dbg(fsl_chan->dev, "event: End-of-link INT\n");
+               dev_dbg(chan->dev, "irq: End-of-link INT\n");
                stat &= ~FSL_DMA_SR_EOLNI;
                xfer_ld_q = 1;
        }
 
        if (update_cookie)
-               fsl_dma_update_completed_cookie(fsl_chan);
+               fsl_dma_update_completed_cookie(chan);
        if (xfer_ld_q)
-               fsl_chan_xfer_ld_queue(fsl_chan);
+               fsl_chan_xfer_ld_queue(chan);
        if (stat)
-               dev_dbg(fsl_chan->dev, "event: unhandled sr 0x%02x\n",
-                                       stat);
+               dev_dbg(chan->dev, "irq: unhandled sr 0x%02x\n", stat);
 
-       dev_dbg(fsl_chan->dev, "event: Exit\n");
-       tasklet_schedule(&fsl_chan->tasklet);
+       dev_dbg(chan->dev, "irq: Exit\n");
+       tasklet_schedule(&chan->tasklet);
        return IRQ_HANDLED;
 }
 
-static irqreturn_t fsl_dma_do_interrupt(int irq, void *data)
+static void dma_do_tasklet(unsigned long data)
 {
-       struct fsl_dma_device *fdev = (struct fsl_dma_device *)data;
-       u32 gsr;
-       int ch_nr;
+       struct fsldma_chan *chan = (struct fsldma_chan *)data;
+       fsl_chan_ld_cleanup(chan);
+}
+
+static irqreturn_t fsldma_ctrl_irq(int irq, void *data)
+{
+       struct fsldma_device *fdev = data;
+       struct fsldma_chan *chan;
+       unsigned int handled = 0;
+       u32 gsr, mask;
+       int i;
 
-       gsr = (fdev->feature & FSL_DMA_BIG_ENDIAN) ? in_be32(fdev->reg_base)
-                       : in_le32(fdev->reg_base);
-       ch_nr = (32 - ffs(gsr)) / 8;
+       gsr = (fdev->feature & FSL_DMA_BIG_ENDIAN) ? in_be32(fdev->regs)
+                                                  : in_le32(fdev->regs);
+       mask = 0xff000000;
+       dev_dbg(fdev->dev, "IRQ: gsr 0x%.8x\n", gsr);
+
+       for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
+               chan = fdev->chan[i];
+               if (!chan)
+                       continue;
+
+               if (gsr & mask) {
+                       dev_dbg(fdev->dev, "IRQ: chan %d\n", chan->id);
+                       fsldma_chan_irq(irq, chan);
+                       handled++;
+               }
 
-       return fdev->chan[ch_nr] ? fsl_dma_chan_do_interrupt(irq,
-                       fdev->chan[ch_nr]) : IRQ_NONE;
+               gsr &= ~mask;
+               mask >>= 8;
+       }
+
+       return IRQ_RETVAL(handled);
 }
 
-static void dma_do_tasklet(unsigned long data)
+static void fsldma_free_irqs(struct fsldma_device *fdev)
 {
-       struct fsl_dma_chan *fsl_chan = (struct fsl_dma_chan *)data;
-       fsl_chan_ld_cleanup(fsl_chan);
+       struct fsldma_chan *chan;
+       int i;
+
+       if (fdev->irq != NO_IRQ) {
+               dev_dbg(fdev->dev, "free per-controller IRQ\n");
+               free_irq(fdev->irq, fdev);
+               return;
+       }
+
+       for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
+               chan = fdev->chan[i];
+               if (chan && chan->irq != NO_IRQ) {
+                       dev_dbg(fdev->dev, "free channel %d IRQ\n", chan->id);
+                       free_irq(chan->irq, chan);
+               }
+       }
 }
 
-static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
+static int fsldma_request_irqs(struct fsldma_device *fdev)
+{
+       struct fsldma_chan *chan;
+       int ret;
+       int i;
+
+       /* if we have a per-controller IRQ, use that */
+       if (fdev->irq != NO_IRQ) {
+               dev_dbg(fdev->dev, "request per-controller IRQ\n");
+               ret = request_irq(fdev->irq, fsldma_ctrl_irq, IRQF_SHARED,
+                                 "fsldma-controller", fdev);
+               return ret;
+       }
+
+       /* no per-controller IRQ, use the per-channel IRQs */
+       for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
+               chan = fdev->chan[i];
+               if (!chan)
+                       continue;
+
+               if (chan->irq == NO_IRQ) {
+                       dev_err(fdev->dev, "no interrupts property defined for "
+                                          "DMA channel %d. Please fix your "
+                                          "device tree\n", chan->id);
+                       ret = -ENODEV;
+                       goto out_unwind;
+               }
+
+               dev_dbg(fdev->dev, "request channel %d IRQ\n", chan->id);
+               ret = request_irq(chan->irq, fsldma_chan_irq, IRQF_SHARED,
+                                 "fsldma-chan", chan);
+               if (ret) {
+                       dev_err(fdev->dev, "unable to request IRQ for DMA "
+                                          "channel %d\n", chan->id);
+                       goto out_unwind;
+               }
+       }
+
+       return 0;
+
+out_unwind:
+       for (/* none */; i >= 0; i--) {
+               chan = fdev->chan[i];
+               if (!chan)
+                       continue;
+
+               if (chan->irq == NO_IRQ)
+                       continue;
+
+               free_irq(chan->irq, chan);
+       }
+
+       return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/* OpenFirmware Subsystem                                                     */
+/*----------------------------------------------------------------------------*/
+
+static int __devinit fsl_dma_chan_probe(struct fsldma_device *fdev,
        struct device_node *node, u32 feature, const char *compatible)
 {
-       struct fsl_dma_chan *new_fsl_chan;
+       struct fsldma_chan *chan;
+       struct resource res;
        int err;
 
        /* alloc channel */
-       new_fsl_chan = kzalloc(sizeof(struct fsl_dma_chan), GFP_KERNEL);
-       if (!new_fsl_chan) {
-               dev_err(fdev->dev, "No free memory for allocating "
-                               "dma channels!\n");
-               return -ENOMEM;
+       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+       if (!chan) {
+               dev_err(fdev->dev, "no free memory for DMA channels!\n");
+               err = -ENOMEM;
+               goto out_return;
        }
 
-       /* get dma channel register base */
-       err = of_address_to_resource(node, 0, &new_fsl_chan->reg);
-       if (err) {
-               dev_err(fdev->dev, "Can't get %s property 'reg'\n",
-                               node->full_name);
-               goto err_no_reg;
+       /* ioremap registers for use */
+       chan->regs = of_iomap(node, 0);
+       if (!chan->regs) {
+               dev_err(fdev->dev, "unable to ioremap registers\n");
+               err = -ENOMEM;
+               goto out_free_chan;
        }
 
-       new_fsl_chan->feature = feature;
+       err = of_address_to_resource(node, 0, &res);
+       if (err) {
+               dev_err(fdev->dev, "unable to find 'reg' property\n");
+               goto out_iounmap_regs;
+       }
 
+       chan->feature = feature;
        if (!fdev->feature)
-               fdev->feature = new_fsl_chan->feature;
+               fdev->feature = chan->feature;
 
-       /* If the DMA device's feature is different than its channels',
-        * report the bug.
+       /*
+        * If the DMA device's feature is different than the feature
+        * of its channels, report the bug
         */
-       WARN_ON(fdev->feature != new_fsl_chan->feature);
+       WARN_ON(fdev->feature != chan->feature);
 
-       new_fsl_chan->dev = fdev->dev;
-       new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start,
-                       new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
-
-       new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7;
-       if (new_fsl_chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
-               dev_err(fdev->dev, "There is no %d channel!\n",
-                               new_fsl_chan->id);
+       chan->dev = fdev->dev;
+       chan->id = ((res.start - 0x100) & 0xfff) >> 7;
+       if (chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
+               dev_err(fdev->dev, "too many channels for device\n");
                err = -EINVAL;
-               goto err_no_chan;
+               goto out_iounmap_regs;
        }
-       fdev->chan[new_fsl_chan->id] = new_fsl_chan;
-       tasklet_init(&new_fsl_chan->tasklet, dma_do_tasklet,
-                       (unsigned long)new_fsl_chan);
 
-       /* Init the channel */
-       dma_init(new_fsl_chan);
+       fdev->chan[chan->id] = chan;
+       tasklet_init(&chan->tasklet, dma_do_tasklet, (unsigned long)chan);
+
+       /* Initialize the channel */
+       dma_init(chan);
 
        /* Clear cdar registers */
-       set_cdar(new_fsl_chan, 0);
+       set_cdar(chan, 0);
 
-       switch (new_fsl_chan->feature & FSL_DMA_IP_MASK) {
+       switch (chan->feature & FSL_DMA_IP_MASK) {
        case FSL_DMA_IP_85XX:
-               new_fsl_chan->toggle_ext_pause = fsl_chan_toggle_ext_pause;
+               chan->toggle_ext_pause = fsl_chan_toggle_ext_pause;
        case FSL_DMA_IP_83XX:
-               new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start;
-               new_fsl_chan->set_src_loop_size = fsl_chan_set_src_loop_size;
-               new_fsl_chan->set_dest_loop_size = fsl_chan_set_dest_loop_size;
-               new_fsl_chan->set_request_count = fsl_chan_set_request_count;
+               chan->toggle_ext_start = fsl_chan_toggle_ext_start;
+               chan->set_src_loop_size = fsl_chan_set_src_loop_size;
+               chan->set_dst_loop_size = fsl_chan_set_dst_loop_size;
+               chan->set_request_count = fsl_chan_set_request_count;
        }
 
-       spin_lock_init(&new_fsl_chan->desc_lock);
-       INIT_LIST_HEAD(&new_fsl_chan->ld_queue);
+       spin_lock_init(&chan->desc_lock);
+       INIT_LIST_HEAD(&chan->ld_pending);
+       INIT_LIST_HEAD(&chan->ld_running);
+
+       chan->common.device = &fdev->common;
 
-       new_fsl_chan->common.device = &fdev->common;
+       /* find the IRQ line, if it exists in the device tree */
+       chan->irq = irq_of_parse_and_map(node, 0);
 
        /* Add the channel to DMA device channel list */
-       list_add_tail(&new_fsl_chan->common.device_node,
-                       &fdev->common.channels);
+       list_add_tail(&chan->common.device_node, &fdev->common.channels);
        fdev->common.chancnt++;
 
-       new_fsl_chan->irq = irq_of_parse_and_map(node, 0);
-       if (new_fsl_chan->irq != NO_IRQ) {
-               err = request_irq(new_fsl_chan->irq,
-                                       &fsl_dma_chan_do_interrupt, IRQF_SHARED,
-                                       "fsldma-channel", new_fsl_chan);
-               if (err) {
-                       dev_err(fdev->dev, "DMA channel %s request_irq error "
-                               "with return %d\n", node->full_name, err);
-                       goto err_no_irq;
-               }
-       }
-
-       dev_info(fdev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id,
-                compatible,
-                new_fsl_chan->irq != NO_IRQ ? new_fsl_chan->irq : fdev->irq);
+       dev_info(fdev->dev, "#%d (%s), irq %d\n", chan->id, compatible,
+                chan->irq != NO_IRQ ? chan->irq : fdev->irq);
 
        return 0;
 
-err_no_irq:
-       list_del(&new_fsl_chan->common.device_node);
-err_no_chan:
-       iounmap(new_fsl_chan->reg_base);
-err_no_reg:
-       kfree(new_fsl_chan);
+out_iounmap_regs:
+       iounmap(chan->regs);
+out_free_chan:
+       kfree(chan);
+out_return:
        return err;
 }
 
-static void fsl_dma_chan_remove(struct fsl_dma_chan *fchan)
+static void fsl_dma_chan_remove(struct fsldma_chan *chan)
 {
-       if (fchan->irq != NO_IRQ)
-               free_irq(fchan->irq, fchan);
-       list_del(&fchan->common.device_node);
-       iounmap(fchan->reg_base);
-       kfree(fchan);
+       irq_dispose_mapping(chan->irq);
+       list_del(&chan->common.device_node);
+       iounmap(chan->regs);
+       kfree(chan);
 }
 
-static int __devinit of_fsl_dma_probe(struct of_device *dev,
+static int __devinit fsldma_of_probe(struct of_device *op,
                        const struct of_device_id *match)
 {
-       int err;
-       struct fsl_dma_device *fdev;
+       struct fsldma_device *fdev;
        struct device_node *child;
+       int err;
 
-       fdev = kzalloc(sizeof(struct fsl_dma_device), GFP_KERNEL);
+       fdev = kzalloc(sizeof(*fdev), GFP_KERNEL);
        if (!fdev) {
-               dev_err(&dev->dev, "No enough memory for 'priv'\n");
-               return -ENOMEM;
+               dev_err(&op->dev, "No enough memory for 'priv'\n");
+               err = -ENOMEM;
+               goto out_return;
        }
-       fdev->dev = &dev->dev;
+
+       fdev->dev = &op->dev;
        INIT_LIST_HEAD(&fdev->common.channels);
 
-       /* get DMA controller register base */
-       err = of_address_to_resource(dev->node, 0, &fdev->reg);
-       if (err) {
-               dev_err(&dev->dev, "Can't get %s property 'reg'\n",
-                               dev->node->full_name);
-               goto err_no_reg;
+       /* ioremap the registers for use */
+       fdev->regs = of_iomap(op->node, 0);
+       if (!fdev->regs) {
+               dev_err(&op->dev, "unable to ioremap registers\n");
+               err = -ENOMEM;
+               goto out_free_fdev;
        }
 
-       dev_info(&dev->dev, "Probe the Freescale DMA driver for %s "
-                       "controller at 0x%llx...\n",
-                       match->compatible, (unsigned long long)fdev->reg.start);
-       fdev->reg_base = ioremap(fdev->reg.start, fdev->reg.end
-                                               - fdev->reg.start + 1);
+       /* map the channel IRQ if it exists, but don't hookup the handler yet */
+       fdev->irq = irq_of_parse_and_map(op->node, 0);
 
        dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask);
        dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask);
@@ -1210,103 +1333,111 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
        fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
        fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg;
        fdev->common.device_terminate_all = fsl_dma_device_terminate_all;
-       fdev->common.dev = &dev->dev;
+       fdev->common.dev = &op->dev;
 
-       fdev->irq = irq_of_parse_and_map(dev->node, 0);
-       if (fdev->irq != NO_IRQ) {
-               err = request_irq(fdev->irq, &fsl_dma_do_interrupt, IRQF_SHARED,
-                                       "fsldma-device", fdev);
-               if (err) {
-                       dev_err(&dev->dev, "DMA device request_irq error "
-                               "with return %d\n", err);
-                       goto err;
-               }
-       }
-
-       dev_set_drvdata(&(dev->dev), fdev);
+       dev_set_drvdata(&op->dev, fdev);
 
-       /* We cannot use of_platform_bus_probe() because there is no
-        * of_platform_bus_remove.  Instead, we manually instantiate every DMA
+       /*
+        * We cannot use of_platform_bus_probe() because there is no
+        * of_platform_bus_remove(). Instead, we manually instantiate every DMA
         * channel object.
         */
-       for_each_child_of_node(dev->node, child) {
-               if (of_device_is_compatible(child, "fsl,eloplus-dma-channel"))
+       for_each_child_of_node(op->node, child) {
+               if (of_device_is_compatible(child, "fsl,eloplus-dma-channel")) {
                        fsl_dma_chan_probe(fdev, child,
                                FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN,
                                "fsl,eloplus-dma-channel");
-               if (of_device_is_compatible(child, "fsl,elo-dma-channel"))
+               }
+
+               if (of_device_is_compatible(child, "fsl,elo-dma-channel")) {
                        fsl_dma_chan_probe(fdev, child,
                                FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN,
                                "fsl,elo-dma-channel");
+               }
+       }
+
+       /*
+        * Hookup the IRQ handler(s)
+        *
+        * If we have a per-controller interrupt, we prefer that to the
+        * per-channel interrupts to reduce the number of shared interrupt
+        * handlers on the same IRQ line
+        */
+       err = fsldma_request_irqs(fdev);
+       if (err) {
+               dev_err(fdev->dev, "unable to request IRQs\n");
+               goto out_free_fdev;
        }
 
        dma_async_device_register(&fdev->common);
        return 0;
 
-err:
-       iounmap(fdev->reg_base);
-err_no_reg:
+out_free_fdev:
+       irq_dispose_mapping(fdev->irq);
        kfree(fdev);
+out_return:
        return err;
 }
 
-static int of_fsl_dma_remove(struct of_device *of_dev)
+static int fsldma_of_remove(struct of_device *op)
 {
-       struct fsl_dma_device *fdev;
+       struct fsldma_device *fdev;
        unsigned int i;
 
-       fdev = dev_get_drvdata(&of_dev->dev);
-
+       fdev = dev_get_drvdata(&op->dev);
        dma_async_device_unregister(&fdev->common);
 
-       for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++)
+       fsldma_free_irqs(fdev);
+
+       for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
                if (fdev->chan[i])
                        fsl_dma_chan_remove(fdev->chan[i]);
+       }
 
-       if (fdev->irq != NO_IRQ)
-               free_irq(fdev->irq, fdev);
-
-       iounmap(fdev->reg_base);
-
+       iounmap(fdev->regs);
+       dev_set_drvdata(&op->dev, NULL);
        kfree(fdev);
-       dev_set_drvdata(&of_dev->dev, NULL);
 
        return 0;
 }
 
-static struct of_device_id of_fsl_dma_ids[] = {
+static const struct of_device_id fsldma_of_ids[] = {
        { .compatible = "fsl,eloplus-dma", },
        { .compatible = "fsl,elo-dma", },
        {}
 };
 
-static struct of_platform_driver of_fsl_dma_driver = {
-       .name = "fsl-elo-dma",
-       .match_table = of_fsl_dma_ids,
-       .probe = of_fsl_dma_probe,
-       .remove = of_fsl_dma_remove,
+static struct of_platform_driver fsldma_of_driver = {
+       .name           = "fsl-elo-dma",
+       .match_table    = fsldma_of_ids,
+       .probe          = fsldma_of_probe,
+       .remove         = fsldma_of_remove,
 };
 
-static __init int of_fsl_dma_init(void)
+/*----------------------------------------------------------------------------*/
+/* Module Init / Exit                                                         */
+/*----------------------------------------------------------------------------*/
+
+static __init int fsldma_init(void)
 {
        int ret;
 
        pr_info("Freescale Elo / Elo Plus DMA driver\n");
 
-       ret = of_register_platform_driver(&of_fsl_dma_driver);
+       ret = of_register_platform_driver(&fsldma_of_driver);
        if (ret)
                pr_err("fsldma: failed to register platform driver\n");
 
        return ret;
 }
 
-static void __exit of_fsl_dma_exit(void)
+static void __exit fsldma_exit(void)
 {
-       of_unregister_platform_driver(&of_fsl_dma_driver);
+       of_unregister_platform_driver(&fsldma_of_driver);
 }
 
-subsys_initcall(of_fsl_dma_init);
-module_exit(of_fsl_dma_exit);
+subsys_initcall(fsldma_init);
+module_exit(fsldma_exit);
 
 MODULE_DESCRIPTION("Freescale Elo / Elo Plus DMA driver");
 MODULE_LICENSE("GPL");
index 0df14cbb8ca335d4ab5b2e444e78a3f42c22709b..cb4d6ff5159766baf7dce594dca6fb57e3d28dd2 100644 (file)
@@ -92,11 +92,9 @@ struct fsl_desc_sw {
        struct list_head node;
        struct list_head tx_list;
        struct dma_async_tx_descriptor async_tx;
-       struct list_head *ld;
-       void *priv;
 } __attribute__((aligned(32)));
 
-struct fsl_dma_chan_regs {
+struct fsldma_chan_regs {
        u32 mr; /* 0x00 - Mode Register */
        u32 sr; /* 0x04 - Status Register */
        u64 cdar;       /* 0x08 - Current descriptor address register */
@@ -106,20 +104,19 @@ struct fsl_dma_chan_regs {
        u64 ndar;       /* 0x24 - Next Descriptor Address Register */
 };
 
-struct fsl_dma_chan;
+struct fsldma_chan;
 #define FSL_DMA_MAX_CHANS_PER_DEVICE 4
 
-struct fsl_dma_device {
-       void __iomem *reg_base; /* DGSR register base */
-       struct resource reg;    /* Resource for register */
+struct fsldma_device {
+       void __iomem *regs;     /* DGSR register base */
        struct device *dev;
        struct dma_device common;
-       struct fsl_dma_chan *chan[FSL_DMA_MAX_CHANS_PER_DEVICE];
+       struct fsldma_chan *chan[FSL_DMA_MAX_CHANS_PER_DEVICE];
        u32 feature;            /* The same as DMA channels */
        int irq;                /* Channel IRQ */
 };
 
-/* Define macros for fsl_dma_chan->feature property */
+/* Define macros for fsldma_chan->feature property */
 #define FSL_DMA_LITTLE_ENDIAN  0x00000000
 #define FSL_DMA_BIG_ENDIAN     0x00000001
 
@@ -130,28 +127,28 @@ struct fsl_dma_device {
 #define FSL_DMA_CHAN_PAUSE_EXT 0x00001000
 #define FSL_DMA_CHAN_START_EXT 0x00002000
 
-struct fsl_dma_chan {
-       struct fsl_dma_chan_regs __iomem *reg_base;
+struct fsldma_chan {
+       struct fsldma_chan_regs __iomem *regs;
        dma_cookie_t completed_cookie;  /* The maximum cookie completed */
        spinlock_t desc_lock;           /* Descriptor operation lock */
-       struct list_head ld_queue;      /* Link descriptors queue */
+       struct list_head ld_pending;    /* Link descriptors queue */
+       struct list_head ld_running;    /* Link descriptors queue */
        struct dma_chan common;         /* DMA common channel */
        struct dma_pool *desc_pool;     /* Descriptors pool */
        struct device *dev;             /* Channel device */
-       struct resource reg;            /* Resource for register */
        int irq;                        /* Channel IRQ */
        int id;                         /* Raw id of this channel */
        struct tasklet_struct tasklet;
        u32 feature;
 
-       void (*toggle_ext_pause)(struct fsl_dma_chan *fsl_chan, int enable);
-       void (*toggle_ext_start)(struct fsl_dma_chan *fsl_chan, int enable);
-       void (*set_src_loop_size)(struct fsl_dma_chan *fsl_chan, int size);
-       void (*set_dest_loop_size)(struct fsl_dma_chan *fsl_chan, int size);
-       void (*set_request_count)(struct fsl_dma_chan *fsl_chan, int size);
+       void (*toggle_ext_pause)(struct fsldma_chan *fsl_chan, int enable);
+       void (*toggle_ext_start)(struct fsldma_chan *fsl_chan, int enable);
+       void (*set_src_loop_size)(struct fsldma_chan *fsl_chan, int size);
+       void (*set_dst_loop_size)(struct fsldma_chan *fsl_chan, int size);
+       void (*set_request_count)(struct fsldma_chan *fsl_chan, int size);
 };
 
-#define to_fsl_chan(chan) container_of(chan, struct fsl_dma_chan, common)
+#define to_fsl_chan(chan) container_of(chan, struct fsldma_chan, common)
 #define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node)
 #define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx)
 
index dcc4ab78b32b79a5d54fc3e8dab9f7af6b61ecc4..af14c9a5b8d40911a00ca666eccf9c42f5a41983 100644 (file)
@@ -71,7 +71,7 @@ static irqreturn_t ioat_dma_do_interrupt(int irq, void *data)
        }
 
        attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET);
-       for_each_bit(bit, &attnstatus, BITS_PER_LONG) {
+       for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) {
                chan = ioat_chan_by_index(instance, bit);
                tasklet_schedule(&chan->cleanup_task);
        }
@@ -94,16 +94,12 @@ static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static void ioat1_cleanup_tasklet(unsigned long data);
-
 /* common channel initialization */
-void ioat_init_channel(struct ioatdma_device *device,
-                      struct ioat_chan_common *chan, int idx,
-                      void (*timer_fn)(unsigned long),
-                      void (*tasklet)(unsigned long),
-                      unsigned long ioat)
+void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *chan, int idx)
 {
        struct dma_device *dma = &device->common;
+       struct dma_chan *c = &chan->common;
+       unsigned long data = (unsigned long) c;
 
        chan->device = device;
        chan->reg_base = device->reg_base + (0x80 * (idx + 1));
@@ -112,14 +108,12 @@ void ioat_init_channel(struct ioatdma_device *device,
        list_add_tail(&chan->common.device_node, &dma->channels);
        device->idx[idx] = chan;
        init_timer(&chan->timer);
-       chan->timer.function = timer_fn;
-       chan->timer.data = ioat;
-       tasklet_init(&chan->cleanup_task, tasklet, ioat);
+       chan->timer.function = device->timer_fn;
+       chan->timer.data = data;
+       tasklet_init(&chan->cleanup_task, device->cleanup_fn, data);
        tasklet_disable(&chan->cleanup_task);
 }
 
-static void ioat1_timer_event(unsigned long data);
-
 /**
  * ioat1_dma_enumerate_channels - find and initialize the device's channels
  * @device: the device to be enumerated
@@ -155,10 +149,7 @@ static int ioat1_enumerate_channels(struct ioatdma_device *device)
                if (!ioat)
                        break;
 
-               ioat_init_channel(device, &ioat->base, i,
-                                 ioat1_timer_event,
-                                 ioat1_cleanup_tasklet,
-                                 (unsigned long) ioat);
+               ioat_init_channel(device, &ioat->base, i);
                ioat->xfercap = xfercap;
                spin_lock_init(&ioat->desc_lock);
                INIT_LIST_HEAD(&ioat->free_desc);
@@ -532,12 +523,12 @@ ioat1_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dest,
        return &desc->txd;
 }
 
-static void ioat1_cleanup_tasklet(unsigned long data)
+static void ioat1_cleanup_event(unsigned long data)
 {
-       struct ioat_dma_chan *chan = (void *)data;
+       struct ioat_dma_chan *ioat = to_ioat_chan((void *) data);
 
-       ioat1_cleanup(chan);
-       writew(IOAT_CHANCTRL_RUN, chan->base.reg_base + IOAT_CHANCTRL_OFFSET);
+       ioat1_cleanup(ioat);
+       writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET);
 }
 
 void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags,
@@ -687,7 +678,7 @@ static void ioat1_cleanup(struct ioat_dma_chan *ioat)
 
 static void ioat1_timer_event(unsigned long data)
 {
-       struct ioat_dma_chan *ioat = (void *) data;
+       struct ioat_dma_chan *ioat = to_ioat_chan((void *) data);
        struct ioat_chan_common *chan = &ioat->base;
 
        dev_dbg(to_dev(chan), "%s: state: %lx\n", __func__, chan->state);
@@ -734,16 +725,17 @@ static void ioat1_timer_event(unsigned long data)
        spin_unlock_bh(&chan->cleanup_lock);
 }
 
-static enum dma_status
-ioat1_dma_is_complete(struct dma_chan *c, dma_cookie_t cookie,
+enum dma_status
+ioat_is_dma_complete(struct dma_chan *c, dma_cookie_t cookie,
                      dma_cookie_t *done, dma_cookie_t *used)
 {
-       struct ioat_dma_chan *ioat = to_ioat_chan(c);
+       struct ioat_chan_common *chan = to_chan_common(c);
+       struct ioatdma_device *device = chan->device;
 
        if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS)
                return DMA_SUCCESS;
 
-       ioat1_cleanup(ioat);
+       device->cleanup_fn((unsigned long) c);
 
        return ioat_is_complete(c, cookie, done, used);
 }
@@ -1199,12 +1191,14 @@ int __devinit ioat1_dma_probe(struct ioatdma_device *device, int dca)
        device->intr_quirk = ioat1_intr_quirk;
        device->enumerate_channels = ioat1_enumerate_channels;
        device->self_test = ioat_dma_self_test;
+       device->timer_fn = ioat1_timer_event;
+       device->cleanup_fn = ioat1_cleanup_event;
        dma = &device->common;
        dma->device_prep_dma_memcpy = ioat1_dma_prep_memcpy;
        dma->device_issue_pending = ioat1_dma_memcpy_issue_pending;
        dma->device_alloc_chan_resources = ioat1_dma_alloc_chan_resources;
        dma->device_free_chan_resources = ioat1_dma_free_chan_resources;
-       dma->device_is_tx_complete = ioat1_dma_is_complete;
+       dma->device_is_tx_complete = ioat_is_dma_complete;
 
        err = ioat_probe(device);
        if (err)
index bbc3e78ef3334f60f025cbb9361d95fb46b38249..4f747a25407437ea1b808cdeed1e4ed96234370f 100644 (file)
@@ -61,7 +61,7 @@
  * @intr_quirk: interrupt setup quirk (for ioat_v1 devices)
  * @enumerate_channels: hw version specific channel enumeration
  * @reset_hw: hw version specific channel (re)initialization
- * @cleanup_tasklet: select between the v2 and v3 cleanup routines
+ * @cleanup_fn: select between the v2 and v3 cleanup routines
  * @timer_fn: select between the v2 and v3 timer watchdog routines
  * @self_test: hardware version specific self test for each supported op type
  *
@@ -80,7 +80,7 @@ struct ioatdma_device {
        void (*intr_quirk)(struct ioatdma_device *device);
        int (*enumerate_channels)(struct ioatdma_device *device);
        int (*reset_hw)(struct ioat_chan_common *chan);
-       void (*cleanup_tasklet)(unsigned long data);
+       void (*cleanup_fn)(unsigned long data);
        void (*timer_fn)(unsigned long data);
        int (*self_test)(struct ioatdma_device *device);
 };
@@ -337,10 +337,9 @@ struct dca_provider * __devinit ioat_dca_init(struct pci_dev *pdev,
                                              void __iomem *iobase);
 unsigned long ioat_get_current_completion(struct ioat_chan_common *chan);
 void ioat_init_channel(struct ioatdma_device *device,
-                      struct ioat_chan_common *chan, int idx,
-                      void (*timer_fn)(unsigned long),
-                      void (*tasklet)(unsigned long),
-                      unsigned long ioat);
+                      struct ioat_chan_common *chan, int idx);
+enum dma_status ioat_is_dma_complete(struct dma_chan *c, dma_cookie_t cookie,
+                                    dma_cookie_t *done, dma_cookie_t *used);
 void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags,
                    size_t len, struct ioat_dma_descriptor *hw);
 bool ioat_cleanup_preamble(struct ioat_chan_common *chan,
index 5cc37afe2bc14b829156b76c5d3c142960517886..1ed5d66d7dca0f87f5765bb3ee2242fa684cb7a0 100644 (file)
@@ -51,48 +51,40 @@ MODULE_PARM_DESC(ioat_ring_max_alloc_order,
 
 void __ioat2_issue_pending(struct ioat2_dma_chan *ioat)
 {
-       void * __iomem reg_base = ioat->base.reg_base;
+       struct ioat_chan_common *chan = &ioat->base;
 
-       ioat->pending = 0;
        ioat->dmacount += ioat2_ring_pending(ioat);
        ioat->issued = ioat->head;
        /* make descriptor updates globally visible before notifying channel */
        wmb();
-       writew(ioat->dmacount, reg_base + IOAT_CHAN_DMACOUNT_OFFSET);
-       dev_dbg(to_dev(&ioat->base),
+       writew(ioat->dmacount, chan->reg_base + IOAT_CHAN_DMACOUNT_OFFSET);
+       dev_dbg(to_dev(chan),
                "%s: head: %#x tail: %#x issued: %#x count: %#x\n",
                __func__, ioat->head, ioat->tail, ioat->issued, ioat->dmacount);
 }
 
-void ioat2_issue_pending(struct dma_chan *chan)
+void ioat2_issue_pending(struct dma_chan *c)
 {
-       struct ioat2_dma_chan *ioat = to_ioat2_chan(chan);
+       struct ioat2_dma_chan *ioat = to_ioat2_chan(c);
 
-       spin_lock_bh(&ioat->ring_lock);
-       if (ioat->pending == 1)
+       if (ioat2_ring_pending(ioat)) {
+               spin_lock_bh(&ioat->ring_lock);
                __ioat2_issue_pending(ioat);
-       spin_unlock_bh(&ioat->ring_lock);
+               spin_unlock_bh(&ioat->ring_lock);
+       }
 }
 
 /**
  * ioat2_update_pending - log pending descriptors
  * @ioat: ioat2+ channel
  *
- * set pending to '1' unless pending is already set to '2', pending == 2
- * indicates that submission is temporarily blocked due to an in-flight
- * reset.  If we are already above the ioat_pending_level threshold then
- * just issue pending.
- *
- * called with ring_lock held
+ * Check if the number of unsubmitted descriptors has exceeded the
+ * watermark.  Called with ring_lock held
  */
 static void ioat2_update_pending(struct ioat2_dma_chan *ioat)
 {
-       if (unlikely(ioat->pending == 2))
-               return;
-       else if (ioat2_ring_pending(ioat) > ioat_pending_level)
+       if (ioat2_ring_pending(ioat) > ioat_pending_level)
                __ioat2_issue_pending(ioat);
-       else
-               ioat->pending = 1;
 }
 
 static void __ioat2_start_null_desc(struct ioat2_dma_chan *ioat)
@@ -166,7 +158,7 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete)
                        seen_current = true;
        }
        ioat->tail += i;
-       BUG_ON(!seen_current); /* no active descs have written a completion? */
+       BUG_ON(active && !seen_current); /* no active descs have written a completion? */
 
        chan->last_completion = phys_complete;
        if (ioat->head == ioat->tail) {
@@ -207,9 +199,9 @@ static void ioat2_cleanup(struct ioat2_dma_chan *ioat)
        spin_unlock_bh(&chan->cleanup_lock);
 }
 
-void ioat2_cleanup_tasklet(unsigned long data)
+void ioat2_cleanup_event(unsigned long data)
 {
-       struct ioat2_dma_chan *ioat = (void *) data;
+       struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data);
 
        ioat2_cleanup(ioat);
        writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET);
@@ -291,7 +283,7 @@ static void ioat2_restart_channel(struct ioat2_dma_chan *ioat)
 
 void ioat2_timer_event(unsigned long data)
 {
-       struct ioat2_dma_chan *ioat = (void *) data;
+       struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data);
        struct ioat_chan_common *chan = &ioat->base;
 
        spin_lock_bh(&chan->cleanup_lock);
@@ -397,10 +389,7 @@ int ioat2_enumerate_channels(struct ioatdma_device *device)
                if (!ioat)
                        break;
 
-               ioat_init_channel(device, &ioat->base, i,
-                                 device->timer_fn,
-                                 device->cleanup_tasklet,
-                                 (unsigned long) ioat);
+               ioat_init_channel(device, &ioat->base, i);
                ioat->xfercap_log = xfercap_log;
                spin_lock_init(&ioat->ring_lock);
                if (device->reset_hw(&ioat->base)) {
@@ -546,7 +535,6 @@ int ioat2_alloc_chan_resources(struct dma_chan *c)
        ioat->head = 0;
        ioat->issued = 0;
        ioat->tail = 0;
-       ioat->pending = 0;
        ioat->alloc_order = order;
        spin_unlock_bh(&ioat->ring_lock);
 
@@ -701,7 +689,7 @@ int ioat2_alloc_and_lock(u16 *idx, struct ioat2_dma_chan *ioat, int num_descs)
 
                        mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT);
                        spin_unlock_bh(&chan->cleanup_lock);
-                       device->timer_fn((unsigned long) ioat);
+                       device->timer_fn((unsigned long) &chan->common);
                } else
                        spin_unlock_bh(&chan->cleanup_lock);
                return -ENOMEM;
@@ -785,7 +773,7 @@ void ioat2_free_chan_resources(struct dma_chan *c)
 
        tasklet_disable(&chan->cleanup_task);
        del_timer_sync(&chan->timer);
-       device->cleanup_tasklet((unsigned long) ioat);
+       device->cleanup_fn((unsigned long) c);
        device->reset_hw(chan);
 
        spin_lock_bh(&ioat->ring_lock);
@@ -815,25 +803,9 @@ void ioat2_free_chan_resources(struct dma_chan *c)
 
        chan->last_completion = 0;
        chan->completion_dma = 0;
-       ioat->pending = 0;
        ioat->dmacount = 0;
 }
 
-enum dma_status
-ioat2_is_complete(struct dma_chan *c, dma_cookie_t cookie,
-                    dma_cookie_t *done, dma_cookie_t *used)
-{
-       struct ioat2_dma_chan *ioat = to_ioat2_chan(c);
-       struct ioatdma_device *device = ioat->base.device;
-
-       if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS)
-               return DMA_SUCCESS;
-
-       device->cleanup_tasklet((unsigned long) ioat);
-
-       return ioat_is_complete(c, cookie, done, used);
-}
-
 static ssize_t ring_size_show(struct dma_chan *c, char *page)
 {
        struct ioat2_dma_chan *ioat = to_ioat2_chan(c);
@@ -874,7 +846,7 @@ int __devinit ioat2_dma_probe(struct ioatdma_device *device, int dca)
 
        device->enumerate_channels = ioat2_enumerate_channels;
        device->reset_hw = ioat2_reset_hw;
-       device->cleanup_tasklet = ioat2_cleanup_tasklet;
+       device->cleanup_fn = ioat2_cleanup_event;
        device->timer_fn = ioat2_timer_event;
        device->self_test = ioat_dma_self_test;
        dma = &device->common;
@@ -882,7 +854,7 @@ int __devinit ioat2_dma_probe(struct ioatdma_device *device, int dca)
        dma->device_issue_pending = ioat2_issue_pending;
        dma->device_alloc_chan_resources = ioat2_alloc_chan_resources;
        dma->device_free_chan_resources = ioat2_free_chan_resources;
-       dma->device_is_tx_complete = ioat2_is_complete;
+       dma->device_is_tx_complete = ioat_is_dma_complete;
 
        err = ioat_probe(device);
        if (err)
index 3afad8da43ccf1a71354fc2400cd7eca9beb2901..ef2871fd7868c6e3b2b8d7ec5507cbfa2f06d7d0 100644 (file)
@@ -47,7 +47,6 @@ extern int ioat_ring_alloc_order;
  * @head: allocated index
  * @issued: hardware notification point
  * @tail: cleanup index
- * @pending: lock free indicator for issued != head
  * @dmacount: identical to 'head' except for occasionally resetting to zero
  * @alloc_order: log2 of the number of allocated descriptors
  * @ring: software ring buffer implementation of hardware ring
@@ -61,7 +60,6 @@ struct ioat2_dma_chan {
        u16 tail;
        u16 dmacount;
        u16 alloc_order;
-       int pending;
        struct ioat_ring_ent **ring;
        spinlock_t ring_lock;
 };
@@ -178,12 +176,10 @@ ioat2_dma_prep_memcpy_lock(struct dma_chan *c, dma_addr_t dma_dest,
 void ioat2_issue_pending(struct dma_chan *chan);
 int ioat2_alloc_chan_resources(struct dma_chan *c);
 void ioat2_free_chan_resources(struct dma_chan *c);
-enum dma_status ioat2_is_complete(struct dma_chan *c, dma_cookie_t cookie,
-                                 dma_cookie_t *done, dma_cookie_t *used);
 void __ioat2_restart_chan(struct ioat2_dma_chan *ioat);
 bool reshape_ring(struct ioat2_dma_chan *ioat, int order);
 void __ioat2_issue_pending(struct ioat2_dma_chan *ioat);
-void ioat2_cleanup_tasklet(unsigned long data);
+void ioat2_cleanup_event(unsigned long data);
 void ioat2_timer_event(unsigned long data);
 int ioat2_quiesce(struct ioat_chan_common *chan, unsigned long tmo);
 int ioat2_reset_sync(struct ioat_chan_common *chan, unsigned long tmo);
index 9908c9e94b2d0c9ba150eeefb507e48cb6cc0a5b..26febc56dab111470f0eed860a34faea40554e98 100644 (file)
@@ -293,17 +293,25 @@ static void __cleanup(struct ioat2_dma_chan *ioat, unsigned long phys_complete)
                }
        }
        ioat->tail += i;
-       BUG_ON(!seen_current); /* no active descs have written a completion? */
+       BUG_ON(active && !seen_current); /* no active descs have written a completion? */
        chan->last_completion = phys_complete;
-       if (ioat->head == ioat->tail) {
+
+       active = ioat2_ring_active(ioat);
+       if (active == 0) {
                dev_dbg(to_dev(chan), "%s: cancel completion timeout\n",
                        __func__);
                clear_bit(IOAT_COMPLETION_PENDING, &chan->state);
                mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT);
        }
+       /* 5 microsecond delay per pending descriptor */
+       writew(min((5 * active), IOAT_INTRDELAY_MASK),
+              chan->device->reg_base + IOAT_INTRDELAY_OFFSET);
 }
 
-static void ioat3_cleanup(struct ioat2_dma_chan *ioat)
+/* try to cleanup, but yield (via spin_trylock) to incoming submissions
+ * with the expectation that we will immediately poll again shortly
+ */
+static void ioat3_cleanup_poll(struct ioat2_dma_chan *ioat)
 {
        struct ioat_chan_common *chan = &ioat->base;
        unsigned long phys_complete;
@@ -329,29 +337,41 @@ static void ioat3_cleanup(struct ioat2_dma_chan *ioat)
        spin_unlock_bh(&chan->cleanup_lock);
 }
 
-static void ioat3_cleanup_tasklet(unsigned long data)
+/* run cleanup now because we already delayed the interrupt via INTRDELAY */
+static void ioat3_cleanup_sync(struct ioat2_dma_chan *ioat)
+{
+       struct ioat_chan_common *chan = &ioat->base;
+       unsigned long phys_complete;
+
+       prefetch(chan->completion);
+
+       spin_lock_bh(&chan->cleanup_lock);
+       if (!ioat_cleanup_preamble(chan, &phys_complete)) {
+               spin_unlock_bh(&chan->cleanup_lock);
+               return;
+       }
+       spin_lock_bh(&ioat->ring_lock);
+
+       __cleanup(ioat, phys_complete);
+
+       spin_unlock_bh(&ioat->ring_lock);
+       spin_unlock_bh(&chan->cleanup_lock);
+}
+
+static void ioat3_cleanup_event(unsigned long data)
 {
-       struct ioat2_dma_chan *ioat = (void *) data;
+       struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data);
 
-       ioat3_cleanup(ioat);
-       writew(IOAT_CHANCTRL_RUN | IOAT3_CHANCTRL_COMPL_DCA_EN,
-              ioat->base.reg_base + IOAT_CHANCTRL_OFFSET);
+       ioat3_cleanup_sync(ioat);
+       writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET);
 }
 
 static void ioat3_restart_channel(struct ioat2_dma_chan *ioat)
 {
        struct ioat_chan_common *chan = &ioat->base;
        unsigned long phys_complete;
-       u32 status;
-
-       status = ioat_chansts(chan);
-       if (is_ioat_active(status) || is_ioat_idle(status))
-               ioat_suspend(chan);
-       while (is_ioat_active(status) || is_ioat_idle(status)) {
-               status = ioat_chansts(chan);
-               cpu_relax();
-       }
 
+       ioat2_quiesce(chan, 0);
        if (ioat_cleanup_preamble(chan, &phys_complete))
                __cleanup(ioat, phys_complete);
 
@@ -360,7 +380,7 @@ static void ioat3_restart_channel(struct ioat2_dma_chan *ioat)
 
 static void ioat3_timer_event(unsigned long data)
 {
-       struct ioat2_dma_chan *ioat = (void *) data;
+       struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data);
        struct ioat_chan_common *chan = &ioat->base;
 
        spin_lock_bh(&chan->cleanup_lock);
@@ -426,7 +446,7 @@ ioat3_is_complete(struct dma_chan *c, dma_cookie_t cookie,
        if (ioat_is_complete(c, cookie, done, used) == DMA_SUCCESS)
                return DMA_SUCCESS;
 
-       ioat3_cleanup(ioat);
+       ioat3_cleanup_poll(ioat);
 
        return ioat_is_complete(c, cookie, done, used);
 }
@@ -1239,11 +1259,11 @@ int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca)
 
        if (is_raid_device) {
                dma->device_is_tx_complete = ioat3_is_complete;
-               device->cleanup_tasklet = ioat3_cleanup_tasklet;
+               device->cleanup_fn = ioat3_cleanup_event;
                device->timer_fn = ioat3_timer_event;
        } else {
-               dma->device_is_tx_complete = ioat2_is_complete;
-               device->cleanup_tasklet = ioat2_cleanup_tasklet;
+               dma->device_is_tx_complete = ioat_is_dma_complete;
+               device->cleanup_fn = ioat2_cleanup_event;
                device->timer_fn = ioat2_timer_event;
        }
 
index e8ae63baf5887f6fed78f7d2fa33ffbf61a89301..1391798542b66756b9ac0e811e64a683a65b77e8 100644 (file)
@@ -60,7 +60,7 @@
 #define IOAT_PERPORTOFFSET_OFFSET              0x0A    /* 16-bit */
 
 #define IOAT_INTRDELAY_OFFSET                  0x0C    /* 16-bit */
-#define IOAT_INTRDELAY_INT_DELAY_MASK          0x3FFF  /* Interrupt Delay Time */
+#define IOAT_INTRDELAY_MASK                    0x3FFF  /* Interrupt Delay Time */
 #define IOAT_INTRDELAY_COALESE_SUPPORT         0x8000  /* Interrupt Coalescing Supported */
 
 #define IOAT_DEVICE_STATUS_OFFSET              0x0E    /* 16-bit */
index e80bae1673fa910f2a774fb455b28f4f61b05075..2a446397c8847aa836c203d6586b672cf3b3024f 100644 (file)
@@ -348,6 +348,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
                break;
        case IPU_PIX_FMT_BGRA32:
        case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_ABGR32:
                params->ip.bpp  = 0;
                params->ip.pfs  = 4;
                params->ip.npb  = 7;
@@ -376,20 +377,6 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
                params->ip.wid2 = 7;            /* Blue bit width - 1 */
                params->ip.wid3 = 7;            /* Alpha bit width - 1 */
                break;
-       case IPU_PIX_FMT_ABGR32:
-               params->ip.bpp  = 0;
-               params->ip.pfs  = 4;
-               params->ip.npb  = 7;
-               params->ip.sat  = 2;            /* SAT = 32-bit access */
-               params->ip.ofs0 = 8;            /* Red bit offset */
-               params->ip.ofs1 = 16;           /* Green bit offset */
-               params->ip.ofs2 = 24;           /* Blue bit offset */
-               params->ip.ofs3 = 0;            /* Alpha bit offset */
-               params->ip.wid0 = 7;            /* Red bit width - 1 */
-               params->ip.wid1 = 7;            /* Green bit width - 1 */
-               params->ip.wid2 = 7;            /* Blue bit width - 1 */
-               params->ip.wid3 = 7;            /* Alpha bit width - 1 */
-               break;
        case IPU_PIX_FMT_UYVY:
                params->ip.bpp  = 2;
                params->ip.pfs  = 6;
diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c
new file mode 100644 (file)
index 0000000..3fdf1f4
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
+ * Copyright (C) Semihalf 2009
+ *
+ * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
+ * (defines, structures and comments) was taken from MPC5121 DMA driver
+ * written by Hongjun Chen <hong-jun.chen@freescale.com>.
+ *
+ * Approved as OSADL project by a majority of OSADL members and funded
+ * by OSADL membership fees in 2009;  for details see www.osadl.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; 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+/*
+ * This is initial version of MPC5121 DMA driver. Only memory to memory
+ * transfers are supported (tested using dmatest module).
+ */
+
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <linux/random.h>
+
+/* Number of DMA Transfer descriptors allocated per channel */
+#define MPC_DMA_DESCRIPTORS    64
+
+/* Macro definitions */
+#define MPC_DMA_CHANNELS       64
+#define MPC_DMA_TCD_OFFSET     0x1000
+
+/* Arbitration mode of group and channel */
+#define MPC_DMA_DMACR_EDCG     (1 << 31)
+#define MPC_DMA_DMACR_ERGA     (1 << 3)
+#define MPC_DMA_DMACR_ERCA     (1 << 2)
+
+/* Error codes */
+#define MPC_DMA_DMAES_VLD      (1 << 31)
+#define MPC_DMA_DMAES_GPE      (1 << 15)
+#define MPC_DMA_DMAES_CPE      (1 << 14)
+#define MPC_DMA_DMAES_ERRCHN(err) \
+                               (((err) >> 8) & 0x3f)
+#define MPC_DMA_DMAES_SAE      (1 << 7)
+#define MPC_DMA_DMAES_SOE      (1 << 6)
+#define MPC_DMA_DMAES_DAE      (1 << 5)
+#define MPC_DMA_DMAES_DOE      (1 << 4)
+#define MPC_DMA_DMAES_NCE      (1 << 3)
+#define MPC_DMA_DMAES_SGE      (1 << 2)
+#define MPC_DMA_DMAES_SBE      (1 << 1)
+#define MPC_DMA_DMAES_DBE      (1 << 0)
+
+#define MPC_DMA_TSIZE_1                0x00
+#define MPC_DMA_TSIZE_2                0x01
+#define MPC_DMA_TSIZE_4                0x02
+#define MPC_DMA_TSIZE_16       0x04
+#define MPC_DMA_TSIZE_32       0x05
+
+/* MPC5121 DMA engine registers */
+struct __attribute__ ((__packed__)) mpc_dma_regs {
+       /* 0x00 */
+       u32 dmacr;              /* DMA control register */
+       u32 dmaes;              /* DMA error status */
+       /* 0x08 */
+       u32 dmaerqh;            /* DMA enable request high(channels 63~32) */
+       u32 dmaerql;            /* DMA enable request low(channels 31~0) */
+       u32 dmaeeih;            /* DMA enable error interrupt high(ch63~32) */
+       u32 dmaeeil;            /* DMA enable error interrupt low(ch31~0) */
+       /* 0x18 */
+       u8 dmaserq;             /* DMA set enable request */
+       u8 dmacerq;             /* DMA clear enable request */
+       u8 dmaseei;             /* DMA set enable error interrupt */
+       u8 dmaceei;             /* DMA clear enable error interrupt */
+       /* 0x1c */
+       u8 dmacint;             /* DMA clear interrupt request */
+       u8 dmacerr;             /* DMA clear error */
+       u8 dmassrt;             /* DMA set start bit */
+       u8 dmacdne;             /* DMA clear DONE status bit */
+       /* 0x20 */
+       u32 dmainth;            /* DMA interrupt request high(ch63~32) */
+       u32 dmaintl;            /* DMA interrupt request low(ch31~0) */
+       u32 dmaerrh;            /* DMA error high(ch63~32) */
+       u32 dmaerrl;            /* DMA error low(ch31~0) */
+       /* 0x30 */
+       u32 dmahrsh;            /* DMA hw request status high(ch63~32) */
+       u32 dmahrsl;            /* DMA hardware request status low(ch31~0) */
+       u32 dmaihsa;            /* DMA interrupt high select AXE(ch63~32) */
+       u32 dmailsa;            /* DMA interrupt low select AXE(ch31~0) */
+       /* 0x40 ~ 0xff */
+       u32 reserve0[48];       /* Reserved */
+       /* 0x100 */
+       u8 dchpri[MPC_DMA_CHANNELS];
+       /* DMA channels(0~63) priority */
+};
+
+struct __attribute__ ((__packed__)) mpc_dma_tcd {
+       /* 0x00 */
+       u32 saddr;              /* Source address */
+
+       u32 smod:5;             /* Source address modulo */
+       u32 ssize:3;            /* Source data transfer size */
+       u32 dmod:5;             /* Destination address modulo */
+       u32 dsize:3;            /* Destination data transfer size */
+       u32 soff:16;            /* Signed source address offset */
+
+       /* 0x08 */
+       u32 nbytes;             /* Inner "minor" byte count */
+       u32 slast;              /* Last source address adjustment */
+       u32 daddr;              /* Destination address */
+
+       /* 0x14 */
+       u32 citer_elink:1;      /* Enable channel-to-channel linking on
+                                * minor loop complete
+                                */
+       u32 citer_linkch:6;     /* Link channel for minor loop complete */
+       u32 citer:9;            /* Current "major" iteration count */
+       u32 doff:16;            /* Signed destination address offset */
+
+       /* 0x18 */
+       u32 dlast_sga;          /* Last Destination address adjustment/scatter
+                                * gather address
+                                */
+
+       /* 0x1c */
+       u32 biter_elink:1;      /* Enable channel-to-channel linking on major
+                                * loop complete
+                                */
+       u32 biter_linkch:6;
+       u32 biter:9;            /* Beginning "major" iteration count */
+       u32 bwc:2;              /* Bandwidth control */
+       u32 major_linkch:6;     /* Link channel number */
+       u32 done:1;             /* Channel done */
+       u32 active:1;           /* Channel active */
+       u32 major_elink:1;      /* Enable channel-to-channel linking on major
+                                * loop complete
+                                */
+       u32 e_sg:1;             /* Enable scatter/gather processing */
+       u32 d_req:1;            /* Disable request */
+       u32 int_half:1;         /* Enable an interrupt when major counter is
+                                * half complete
+                                */
+       u32 int_maj:1;          /* Enable an interrupt when major iteration
+                                * count completes
+                                */
+       u32 start:1;            /* Channel start */
+};
+
+struct mpc_dma_desc {
+       struct dma_async_tx_descriptor  desc;
+       struct mpc_dma_tcd              *tcd;
+       dma_addr_t                      tcd_paddr;
+       int                             error;
+       struct list_head                node;
+};
+
+struct mpc_dma_chan {
+       struct dma_chan                 chan;
+       struct list_head                free;
+       struct list_head                prepared;
+       struct list_head                queued;
+       struct list_head                active;
+       struct list_head                completed;
+       struct mpc_dma_tcd              *tcd;
+       dma_addr_t                      tcd_paddr;
+       dma_cookie_t                    completed_cookie;
+
+       /* Lock for this structure */
+       spinlock_t                      lock;
+};
+
+struct mpc_dma {
+       struct dma_device               dma;
+       struct tasklet_struct           tasklet;
+       struct mpc_dma_chan             channels[MPC_DMA_CHANNELS];
+       struct mpc_dma_regs __iomem     *regs;
+       struct mpc_dma_tcd __iomem      *tcd;
+       int                             irq;
+       uint                            error_status;
+
+       /* Lock for error_status field in this structure */
+       spinlock_t                      error_status_lock;
+};
+
+#define DRV_NAME       "mpc512x_dma"
+
+/* Convert struct dma_chan to struct mpc_dma_chan */
+static inline struct mpc_dma_chan *dma_chan_to_mpc_dma_chan(struct dma_chan *c)
+{
+       return container_of(c, struct mpc_dma_chan, chan);
+}
+
+/* Convert struct dma_chan to struct mpc_dma */
+static inline struct mpc_dma *dma_chan_to_mpc_dma(struct dma_chan *c)
+{
+       struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(c);
+       return container_of(mchan, struct mpc_dma, channels[c->chan_id]);
+}
+
+/*
+ * Execute all queued DMA descriptors.
+ *
+ * Following requirements must be met while calling mpc_dma_execute():
+ *     a) mchan->lock is acquired,
+ *     b) mchan->active list is empty,
+ *     c) mchan->queued list contains at least one entry.
+ */
+static void mpc_dma_execute(struct mpc_dma_chan *mchan)
+{
+       struct mpc_dma *mdma = dma_chan_to_mpc_dma(&mchan->chan);
+       struct mpc_dma_desc *first = NULL;
+       struct mpc_dma_desc *prev = NULL;
+       struct mpc_dma_desc *mdesc;
+       int cid = mchan->chan.chan_id;
+
+       /* Move all queued descriptors to active list */
+       list_splice_tail_init(&mchan->queued, &mchan->active);
+
+       /* Chain descriptors into one transaction */
+       list_for_each_entry(mdesc, &mchan->active, node) {
+               if (!first)
+                       first = mdesc;
+
+               if (!prev) {
+                       prev = mdesc;
+                       continue;
+               }
+
+               prev->tcd->dlast_sga = mdesc->tcd_paddr;
+               prev->tcd->e_sg = 1;
+               mdesc->tcd->start = 1;
+
+               prev = mdesc;
+       }
+
+       prev->tcd->start = 0;
+       prev->tcd->int_maj = 1;
+
+       /* Send first descriptor in chain into hardware */
+       memcpy_toio(&mdma->tcd[cid], first->tcd, sizeof(struct mpc_dma_tcd));
+       out_8(&mdma->regs->dmassrt, cid);
+}
+
+/* Handle interrupt on one half of DMA controller (32 channels) */
+static void mpc_dma_irq_process(struct mpc_dma *mdma, u32 is, u32 es, int off)
+{
+       struct mpc_dma_chan *mchan;
+       struct mpc_dma_desc *mdesc;
+       u32 status = is | es;
+       int ch;
+
+       while ((ch = fls(status) - 1) >= 0) {
+               status &= ~(1 << ch);
+               mchan = &mdma->channels[ch + off];
+
+               spin_lock(&mchan->lock);
+
+               /* Check error status */
+               if (es & (1 << ch))
+                       list_for_each_entry(mdesc, &mchan->active, node)
+                               mdesc->error = -EIO;
+
+               /* Execute queued descriptors */
+               list_splice_tail_init(&mchan->active, &mchan->completed);
+               if (!list_empty(&mchan->queued))
+                       mpc_dma_execute(mchan);
+
+               spin_unlock(&mchan->lock);
+       }
+}
+
+/* Interrupt handler */
+static irqreturn_t mpc_dma_irq(int irq, void *data)
+{
+       struct mpc_dma *mdma = data;
+       uint es;
+
+       /* Save error status register */
+       es = in_be32(&mdma->regs->dmaes);
+       spin_lock(&mdma->error_status_lock);
+       if ((es & MPC_DMA_DMAES_VLD) && mdma->error_status == 0)
+               mdma->error_status = es;
+       spin_unlock(&mdma->error_status_lock);
+
+       /* Handle interrupt on each channel */
+       mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
+                                       in_be32(&mdma->regs->dmaerrh), 32);
+       mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmaintl),
+                                       in_be32(&mdma->regs->dmaerrl), 0);
+
+       /* Ack interrupt on all channels */
+       out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
+       out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
+       out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
+       out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
+
+       /* Schedule tasklet */
+       tasklet_schedule(&mdma->tasklet);
+
+       return IRQ_HANDLED;
+}
+
+/* DMA Tasklet */
+static void mpc_dma_tasklet(unsigned long data)
+{
+       struct mpc_dma *mdma = (void *)data;
+       dma_cookie_t last_cookie = 0;
+       struct mpc_dma_chan *mchan;
+       struct mpc_dma_desc *mdesc;
+       struct dma_async_tx_descriptor *desc;
+       unsigned long flags;
+       LIST_HEAD(list);
+       uint es;
+       int i;
+
+       spin_lock_irqsave(&mdma->error_status_lock, flags);
+       es = mdma->error_status;
+       mdma->error_status = 0;
+       spin_unlock_irqrestore(&mdma->error_status_lock, flags);
+
+       /* Print nice error report */
+       if (es) {
+               dev_err(mdma->dma.dev,
+                       "Hardware reported following error(s) on channel %u:\n",
+                                                     MPC_DMA_DMAES_ERRCHN(es));
+
+               if (es & MPC_DMA_DMAES_GPE)
+                       dev_err(mdma->dma.dev, "- Group Priority Error\n");
+               if (es & MPC_DMA_DMAES_CPE)
+                       dev_err(mdma->dma.dev, "- Channel Priority Error\n");
+               if (es & MPC_DMA_DMAES_SAE)
+                       dev_err(mdma->dma.dev, "- Source Address Error\n");
+               if (es & MPC_DMA_DMAES_SOE)
+                       dev_err(mdma->dma.dev, "- Source Offset"
+                                               " Configuration Error\n");
+               if (es & MPC_DMA_DMAES_DAE)
+                       dev_err(mdma->dma.dev, "- Destination Address"
+                                                               " Error\n");
+               if (es & MPC_DMA_DMAES_DOE)
+                       dev_err(mdma->dma.dev, "- Destination Offset"
+                                               " Configuration Error\n");
+               if (es & MPC_DMA_DMAES_NCE)
+                       dev_err(mdma->dma.dev, "- NBytes/Citter"
+                                               " Configuration Error\n");
+               if (es & MPC_DMA_DMAES_SGE)
+                       dev_err(mdma->dma.dev, "- Scatter/Gather"
+                                               " Configuration Error\n");
+               if (es & MPC_DMA_DMAES_SBE)
+                       dev_err(mdma->dma.dev, "- Source Bus Error\n");
+               if (es & MPC_DMA_DMAES_DBE)
+                       dev_err(mdma->dma.dev, "- Destination Bus Error\n");
+       }
+
+       for (i = 0; i < mdma->dma.chancnt; i++) {
+               mchan = &mdma->channels[i];
+
+               /* Get all completed descriptors */
+               spin_lock_irqsave(&mchan->lock, flags);
+               if (!list_empty(&mchan->completed))
+                       list_splice_tail_init(&mchan->completed, &list);
+               spin_unlock_irqrestore(&mchan->lock, flags);
+
+               if (list_empty(&list))
+                       continue;
+
+               /* Execute callbacks and run dependencies */
+               list_for_each_entry(mdesc, &list, node) {
+                       desc = &mdesc->desc;
+
+                       if (desc->callback)
+                               desc->callback(desc->callback_param);
+
+                       last_cookie = desc->cookie;
+                       dma_run_dependencies(desc);
+               }
+
+               /* Free descriptors */
+               spin_lock_irqsave(&mchan->lock, flags);
+               list_splice_tail_init(&list, &mchan->free);
+               mchan->completed_cookie = last_cookie;
+               spin_unlock_irqrestore(&mchan->lock, flags);
+       }
+}
+
+/* Submit descriptor to hardware */
+static dma_cookie_t mpc_dma_tx_submit(struct dma_async_tx_descriptor *txd)
+{
+       struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(txd->chan);
+       struct mpc_dma_desc *mdesc;
+       unsigned long flags;
+       dma_cookie_t cookie;
+
+       mdesc = container_of(txd, struct mpc_dma_desc, desc);
+
+       spin_lock_irqsave(&mchan->lock, flags);
+
+       /* Move descriptor to queue */
+       list_move_tail(&mdesc->node, &mchan->queued);
+
+       /* If channel is idle, execute all queued descriptors */
+       if (list_empty(&mchan->active))
+               mpc_dma_execute(mchan);
+
+       /* Update cookie */
+       cookie = mchan->chan.cookie + 1;
+       if (cookie <= 0)
+               cookie = 1;
+
+       mchan->chan.cookie = cookie;
+       mdesc->desc.cookie = cookie;
+
+       spin_unlock_irqrestore(&mchan->lock, flags);
+
+       return cookie;
+}
+
+/* Alloc channel resources */
+static int mpc_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
+       struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
+       struct mpc_dma_desc *mdesc;
+       struct mpc_dma_tcd *tcd;
+       dma_addr_t tcd_paddr;
+       unsigned long flags;
+       LIST_HEAD(descs);
+       int i;
+
+       /* Alloc DMA memory for Transfer Control Descriptors */
+       tcd = dma_alloc_coherent(mdma->dma.dev,
+                       MPC_DMA_DESCRIPTORS * sizeof(struct mpc_dma_tcd),
+                                                       &tcd_paddr, GFP_KERNEL);
+       if (!tcd)
+               return -ENOMEM;
+
+       /* Alloc descriptors for this channel */
+       for (i = 0; i < MPC_DMA_DESCRIPTORS; i++) {
+               mdesc = kzalloc(sizeof(struct mpc_dma_desc), GFP_KERNEL);
+               if (!mdesc) {
+                       dev_notice(mdma->dma.dev, "Memory allocation error. "
+                                       "Allocated only %u descriptors\n", i);
+                       break;
+               }
+
+               dma_async_tx_descriptor_init(&mdesc->desc, chan);
+               mdesc->desc.flags = DMA_CTRL_ACK;
+               mdesc->desc.tx_submit = mpc_dma_tx_submit;
+
+               mdesc->tcd = &tcd[i];
+               mdesc->tcd_paddr = tcd_paddr + (i * sizeof(struct mpc_dma_tcd));
+
+               list_add_tail(&mdesc->node, &descs);
+       }
+
+       /* Return error only if no descriptors were allocated */
+       if (i == 0) {
+               dma_free_coherent(mdma->dma.dev,
+                       MPC_DMA_DESCRIPTORS * sizeof(struct mpc_dma_tcd),
+                                                               tcd, tcd_paddr);
+               return -ENOMEM;
+       }
+
+       spin_lock_irqsave(&mchan->lock, flags);
+       mchan->tcd = tcd;
+       mchan->tcd_paddr = tcd_paddr;
+       list_splice_tail_init(&descs, &mchan->free);
+       spin_unlock_irqrestore(&mchan->lock, flags);
+
+       /* Enable Error Interrupt */
+       out_8(&mdma->regs->dmaseei, chan->chan_id);
+
+       return 0;
+}
+
+/* Free channel resources */
+static void mpc_dma_free_chan_resources(struct dma_chan *chan)
+{
+       struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
+       struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
+       struct mpc_dma_desc *mdesc, *tmp;
+       struct mpc_dma_tcd *tcd;
+       dma_addr_t tcd_paddr;
+       unsigned long flags;
+       LIST_HEAD(descs);
+
+       spin_lock_irqsave(&mchan->lock, flags);
+
+       /* Channel must be idle */
+       BUG_ON(!list_empty(&mchan->prepared));
+       BUG_ON(!list_empty(&mchan->queued));
+       BUG_ON(!list_empty(&mchan->active));
+       BUG_ON(!list_empty(&mchan->completed));
+
+       /* Move data */
+       list_splice_tail_init(&mchan->free, &descs);
+       tcd = mchan->tcd;
+       tcd_paddr = mchan->tcd_paddr;
+
+       spin_unlock_irqrestore(&mchan->lock, flags);
+
+       /* Free DMA memory used by descriptors */
+       dma_free_coherent(mdma->dma.dev,
+                       MPC_DMA_DESCRIPTORS * sizeof(struct mpc_dma_tcd),
+                                                               tcd, tcd_paddr);
+
+       /* Free descriptors */
+       list_for_each_entry_safe(mdesc, tmp, &descs, node)
+               kfree(mdesc);
+
+       /* Disable Error Interrupt */
+       out_8(&mdma->regs->dmaceei, chan->chan_id);
+}
+
+/* Send all pending descriptor to hardware */
+static void mpc_dma_issue_pending(struct dma_chan *chan)
+{
+       /*
+        * We are posting descriptors to the hardware as soon as
+        * they are ready, so this function does nothing.
+        */
+}
+
+/* Check request completion status */
+static enum dma_status
+mpc_dma_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie,
+                                       dma_cookie_t *done, dma_cookie_t *used)
+{
+       struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
+       unsigned long flags;
+       dma_cookie_t last_used;
+       dma_cookie_t last_complete;
+
+       spin_lock_irqsave(&mchan->lock, flags);
+       last_used = mchan->chan.cookie;
+       last_complete = mchan->completed_cookie;
+       spin_unlock_irqrestore(&mchan->lock, flags);
+
+       if (done)
+               *done = last_complete;
+
+       if (used)
+               *used = last_used;
+
+       return dma_async_is_complete(cookie, last_complete, last_used);
+}
+
+/* Prepare descriptor for memory to memory copy */
+static struct dma_async_tx_descriptor *
+mpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
+                                       size_t len, unsigned long flags)
+{
+       struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
+       struct mpc_dma_desc *mdesc = NULL;
+       struct mpc_dma_tcd *tcd;
+       unsigned long iflags;
+
+       /* Get free descriptor */
+       spin_lock_irqsave(&mchan->lock, iflags);
+       if (!list_empty(&mchan->free)) {
+               mdesc = list_first_entry(&mchan->free, struct mpc_dma_desc,
+                                                                       node);
+               list_del(&mdesc->node);
+       }
+       spin_unlock_irqrestore(&mchan->lock, iflags);
+
+       if (!mdesc)
+               return NULL;
+
+       mdesc->error = 0;
+       tcd = mdesc->tcd;
+
+       /* Prepare Transfer Control Descriptor for this transaction */
+       memset(tcd, 0, sizeof(struct mpc_dma_tcd));
+
+       if (IS_ALIGNED(src | dst | len, 32)) {
+               tcd->ssize = MPC_DMA_TSIZE_32;
+               tcd->dsize = MPC_DMA_TSIZE_32;
+               tcd->soff = 32;
+               tcd->doff = 32;
+       } else if (IS_ALIGNED(src | dst | len, 16)) {
+               tcd->ssize = MPC_DMA_TSIZE_16;
+               tcd->dsize = MPC_DMA_TSIZE_16;
+               tcd->soff = 16;
+               tcd->doff = 16;
+       } else if (IS_ALIGNED(src | dst | len, 4)) {
+               tcd->ssize = MPC_DMA_TSIZE_4;
+               tcd->dsize = MPC_DMA_TSIZE_4;
+               tcd->soff = 4;
+               tcd->doff = 4;
+       } else if (IS_ALIGNED(src | dst | len, 2)) {
+               tcd->ssize = MPC_DMA_TSIZE_2;
+               tcd->dsize = MPC_DMA_TSIZE_2;
+               tcd->soff = 2;
+               tcd->doff = 2;
+       } else {
+               tcd->ssize = MPC_DMA_TSIZE_1;
+               tcd->dsize = MPC_DMA_TSIZE_1;
+               tcd->soff = 1;
+               tcd->doff = 1;
+       }
+
+       tcd->saddr = src;
+       tcd->daddr = dst;
+       tcd->nbytes = len;
+       tcd->biter = 1;
+       tcd->citer = 1;
+
+       /* Place descriptor in prepared list */
+       spin_lock_irqsave(&mchan->lock, iflags);
+       list_add_tail(&mdesc->node, &mchan->prepared);
+       spin_unlock_irqrestore(&mchan->lock, iflags);
+
+       return &mdesc->desc;
+}
+
+static int __devinit mpc_dma_probe(struct of_device *op,
+                                       const struct of_device_id *match)
+{
+       struct device_node *dn = op->node;
+       struct device *dev = &op->dev;
+       struct dma_device *dma;
+       struct mpc_dma *mdma;
+       struct mpc_dma_chan *mchan;
+       struct resource res;
+       ulong regs_start, regs_size;
+       int retval, i;
+
+       mdma = devm_kzalloc(dev, sizeof(struct mpc_dma), GFP_KERNEL);
+       if (!mdma) {
+               dev_err(dev, "Memory exhausted!\n");
+               return -ENOMEM;
+       }
+
+       mdma->irq = irq_of_parse_and_map(dn, 0);
+       if (mdma->irq == NO_IRQ) {
+               dev_err(dev, "Error mapping IRQ!\n");
+               return -EINVAL;
+       }
+
+       retval = of_address_to_resource(dn, 0, &res);
+       if (retval) {
+               dev_err(dev, "Error parsing memory region!\n");
+               return retval;
+       }
+
+       regs_start = res.start;
+       regs_size = res.end - res.start + 1;
+
+       if (!devm_request_mem_region(dev, regs_start, regs_size, DRV_NAME)) {
+               dev_err(dev, "Error requesting memory region!\n");
+               return -EBUSY;
+       }
+
+       mdma->regs = devm_ioremap(dev, regs_start, regs_size);
+       if (!mdma->regs) {
+               dev_err(dev, "Error mapping memory region!\n");
+               return -ENOMEM;
+       }
+
+       mdma->tcd = (struct mpc_dma_tcd *)((u8 *)(mdma->regs)
+                                                       + MPC_DMA_TCD_OFFSET);
+
+       retval = devm_request_irq(dev, mdma->irq, &mpc_dma_irq, 0, DRV_NAME,
+                                                                       mdma);
+       if (retval) {
+               dev_err(dev, "Error requesting IRQ!\n");
+               return -EINVAL;
+       }
+
+       spin_lock_init(&mdma->error_status_lock);
+
+       dma = &mdma->dma;
+       dma->dev = dev;
+       dma->chancnt = MPC_DMA_CHANNELS;
+       dma->device_alloc_chan_resources = mpc_dma_alloc_chan_resources;
+       dma->device_free_chan_resources = mpc_dma_free_chan_resources;
+       dma->device_issue_pending = mpc_dma_issue_pending;
+       dma->device_is_tx_complete = mpc_dma_is_tx_complete;
+       dma->device_prep_dma_memcpy = mpc_dma_prep_memcpy;
+
+       INIT_LIST_HEAD(&dma->channels);
+       dma_cap_set(DMA_MEMCPY, dma->cap_mask);
+
+       for (i = 0; i < dma->chancnt; i++) {
+               mchan = &mdma->channels[i];
+
+               mchan->chan.device = dma;
+               mchan->chan.chan_id = i;
+               mchan->chan.cookie = 1;
+               mchan->completed_cookie = mchan->chan.cookie;
+
+               INIT_LIST_HEAD(&mchan->free);
+               INIT_LIST_HEAD(&mchan->prepared);
+               INIT_LIST_HEAD(&mchan->queued);
+               INIT_LIST_HEAD(&mchan->active);
+               INIT_LIST_HEAD(&mchan->completed);
+
+               spin_lock_init(&mchan->lock);
+               list_add_tail(&mchan->chan.device_node, &dma->channels);
+       }
+
+       tasklet_init(&mdma->tasklet, mpc_dma_tasklet, (unsigned long)mdma);
+
+       /*
+        * Configure DMA Engine:
+        * - Dynamic clock,
+        * - Round-robin group arbitration,
+        * - Round-robin channel arbitration.
+        */
+       out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
+                               MPC_DMA_DMACR_ERGA | MPC_DMA_DMACR_ERCA);
+
+       /* Disable hardware DMA requests */
+       out_be32(&mdma->regs->dmaerqh, 0);
+       out_be32(&mdma->regs->dmaerql, 0);
+
+       /* Disable error interrupts */
+       out_be32(&mdma->regs->dmaeeih, 0);
+       out_be32(&mdma->regs->dmaeeil, 0);
+
+       /* Clear interrupts status */
+       out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
+       out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
+       out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
+       out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
+
+       /* Route interrupts to IPIC */
+       out_be32(&mdma->regs->dmaihsa, 0);
+       out_be32(&mdma->regs->dmailsa, 0);
+
+       /* Register DMA engine */
+       dev_set_drvdata(dev, mdma);
+       retval = dma_async_device_register(dma);
+       if (retval) {
+               devm_free_irq(dev, mdma->irq, mdma);
+               irq_dispose_mapping(mdma->irq);
+       }
+
+       return retval;
+}
+
+static int __devexit mpc_dma_remove(struct of_device *op)
+{
+       struct device *dev = &op->dev;
+       struct mpc_dma *mdma = dev_get_drvdata(dev);
+
+       dma_async_device_unregister(&mdma->dma);
+       devm_free_irq(dev, mdma->irq, mdma);
+       irq_dispose_mapping(mdma->irq);
+
+       return 0;
+}
+
+static struct of_device_id mpc_dma_match[] = {
+       { .compatible = "fsl,mpc5121-dma", },
+       {},
+};
+
+static struct of_platform_driver mpc_dma_driver = {
+       .match_table    = mpc_dma_match,
+       .probe          = mpc_dma_probe,
+       .remove         = __devexit_p(mpc_dma_remove),
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init mpc_dma_init(void)
+{
+       return of_register_platform_driver(&mpc_dma_driver);
+}
+module_init(mpc_dma_init);
+
+static void __exit mpc_dma_exit(void)
+{
+       of_unregister_platform_driver(&mpc_dma_driver);
+}
+module_exit(mpc_dma_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Piotr Ziecik <kosmo@semihalf.com>");
index 0a3478e910f055aca4bdc22bacdfb221ccd5ee40..e69d87f24a257210c14381722617246a1fd184d7 100644 (file)
@@ -4940,7 +4940,7 @@ out_free:
        return ret;
 }
 
-static struct of_device_id __devinitdata ppc440spe_adma_of_match[] = {
+static const struct of_device_id ppc440spe_adma_of_match[] __devinitconst = {
        { .compatible   = "ibm,dma-440spe", },
        { .compatible   = "amcc,xor-accelerator", },
        {},
index b75ce8b84c46ac2622af1000b3ff6095ffefaf43..5d17e09cb625412beaf0e8275f39142b7006a4c1 100644 (file)
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
-#include <cpu/dma.h>
-#include <asm/dma-sh.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/dmaengine.h>
+
 #include "shdma.h"
 
 /* DMA descriptor control */
@@ -38,30 +40,32 @@ enum sh_dmae_desc_status {
 };
 
 #define NR_DESCS_PER_CHANNEL 32
-/*
- * Define the default configuration for dual address memory-memory transfer.
- * The 0x400 value represents auto-request, external->external.
- *
- * And this driver set 4byte burst mode.
- * If you want to change mode, you need to change RS_DEFAULT of value.
- * (ex 1byte burst mode -> (RS_DUAL & ~TS_32)
- */
-#define RS_DEFAULT  (RS_DUAL)
+/* Default MEMCPY transfer size = 2^2 = 4 bytes */
+#define LOG2_DEFAULT_XFER_SIZE 2
 
 /* A bitmask with bits enough for enum sh_dmae_slave_chan_id */
 static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SHDMA_SLAVE_NUMBER)];
 
 static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
 
-#define SH_DMAC_CHAN_BASE(id) (dma_base_addr[id])
 static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
 {
-       ctrl_outl(data, SH_DMAC_CHAN_BASE(sh_dc->id) + reg);
+       __raw_writel(data, sh_dc->base + reg / sizeof(u32));
 }
 
 static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
 {
-       return ctrl_inl(SH_DMAC_CHAN_BASE(sh_dc->id) + reg);
+       return __raw_readl(sh_dc->base + reg / sizeof(u32));
+}
+
+static u16 dmaor_read(struct sh_dmae_device *shdev)
+{
+       return __raw_readw(shdev->chan_reg + DMAOR / sizeof(u32));
+}
+
+static void dmaor_write(struct sh_dmae_device *shdev, u16 data)
+{
+       __raw_writew(data, shdev->chan_reg + DMAOR / sizeof(u32));
 }
 
 /*
@@ -69,24 +73,23 @@ static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
  *
  * SH7780 has two DMAOR register
  */
-static void sh_dmae_ctl_stop(int id)
+static void sh_dmae_ctl_stop(struct sh_dmae_device *shdev)
 {
-       unsigned short dmaor = dmaor_read_reg(id);
+       unsigned short dmaor = dmaor_read(shdev);
 
-       dmaor &= ~(DMAOR_NMIF | DMAOR_AE);
-       dmaor_write_reg(id, dmaor);
+       dmaor_write(shdev, dmaor & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME));
 }
 
-static int sh_dmae_rst(int id)
+static int sh_dmae_rst(struct sh_dmae_device *shdev)
 {
        unsigned short dmaor;
 
-       sh_dmae_ctl_stop(id);
-       dmaor = dmaor_read_reg(id) | DMAOR_INIT;
+       sh_dmae_ctl_stop(shdev);
+       dmaor = dmaor_read(shdev) | shdev->pdata->dmaor_init;
 
-       dmaor_write_reg(id, dmaor);
-       if (dmaor_read_reg(id) & (DMAOR_AE | DMAOR_NMIF)) {
-               pr_warning(KERN_ERR "dma-sh: Can't initialize DMAOR.\n");
+       dmaor_write(shdev, dmaor);
+       if (dmaor_read(shdev) & (DMAOR_AE | DMAOR_NMIF)) {
+               pr_warning("dma-sh: Can't initialize DMAOR.\n");
                return -EINVAL;
        }
        return 0;
@@ -102,13 +105,36 @@ static bool dmae_is_busy(struct sh_dmae_chan *sh_chan)
        return false; /* waiting */
 }
 
-static unsigned int ts_shift[] = TS_SHIFT;
-static inline unsigned int calc_xmit_shift(u32 chcr)
+static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
 {
-       int cnt = ((chcr & CHCR_TS_LOW_MASK) >> CHCR_TS_LOW_SHIFT) |
-               ((chcr & CHCR_TS_HIGH_MASK) >> CHCR_TS_HIGH_SHIFT);
+       struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
+                                               struct sh_dmae_device, common);
+       struct sh_dmae_pdata *pdata = shdev->pdata;
+       int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) |
+               ((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift);
+
+       if (cnt >= pdata->ts_shift_num)
+               cnt = 0;
 
-       return ts_shift[cnt];
+       return pdata->ts_shift[cnt];
+}
+
+static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
+{
+       struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
+                                               struct sh_dmae_device, common);
+       struct sh_dmae_pdata *pdata = shdev->pdata;
+       int i;
+
+       for (i = 0; i < pdata->ts_shift_num; i++)
+               if (pdata->ts_shift[i] == l2size)
+                       break;
+
+       if (i == pdata->ts_shift_num)
+               i = 0;
+
+       return ((i << pdata->ts_low_shift) & pdata->ts_low_mask) |
+               ((i << pdata->ts_high_shift) & pdata->ts_high_mask);
 }
 
 static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw)
@@ -136,8 +162,13 @@ static void dmae_halt(struct sh_dmae_chan *sh_chan)
 
 static void dmae_init(struct sh_dmae_chan *sh_chan)
 {
-       u32 chcr = RS_DEFAULT; /* default is DUAL mode */
-       sh_chan->xmit_shift = calc_xmit_shift(chcr);
+       /*
+        * Default configuration for dual address memory-memory transfer.
+        * 0x400 represents auto-request.
+        */
+       u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan,
+                                                  LOG2_DEFAULT_XFER_SIZE);
+       sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr);
        sh_dmae_writel(sh_chan, chcr, CHCR);
 }
 
@@ -147,37 +178,26 @@ static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val)
        if (dmae_is_busy(sh_chan))
                return -EBUSY;
 
-       sh_chan->xmit_shift = calc_xmit_shift(val);
+       sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val);
        sh_dmae_writel(sh_chan, val, CHCR);
 
        return 0;
 }
 
-#define DMARS_SHIFT    8
-#define DMARS_CHAN_MSK 0x01
 static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
 {
-       u32 addr;
-       int shift = 0;
+       struct sh_dmae_device *shdev = container_of(sh_chan->common.device,
+                                               struct sh_dmae_device, common);
+       struct sh_dmae_pdata *pdata = shdev->pdata;
+       struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id];
+       u16 __iomem *addr = shdev->dmars + chan_pdata->dmars / sizeof(u16);
+       int shift = chan_pdata->dmars_bit;
 
        if (dmae_is_busy(sh_chan))
                return -EBUSY;
 
-       if (sh_chan->id & DMARS_CHAN_MSK)
-               shift = DMARS_SHIFT;
-
-       if (sh_chan->id < 6)
-               /* DMA0RS0 - DMA0RS2 */
-               addr = SH_DMARS_BASE0 + (sh_chan->id / 2) * 4;
-#ifdef SH_DMARS_BASE1
-       else if (sh_chan->id < 12)
-               /* DMA1RS0 - DMA1RS2 */
-               addr = SH_DMARS_BASE1 + ((sh_chan->id - 6) / 2) * 4;
-#endif
-       else
-               return -EINVAL;
-
-       ctrl_outw((val << shift) | (ctrl_inw(addr) & (0xFF00 >> shift)), addr);
+       __raw_writew((__raw_readw(addr) & (0xff00 >> shift)) | (val << shift),
+                    addr);
 
        return 0;
 }
@@ -251,15 +271,15 @@ static struct sh_dmae_slave_config *sh_dmae_find_slave(
        struct dma_device *dma_dev = sh_chan->common.device;
        struct sh_dmae_device *shdev = container_of(dma_dev,
                                        struct sh_dmae_device, common);
-       struct sh_dmae_pdata *pdata = &shdev->pdata;
+       struct sh_dmae_pdata *pdata = shdev->pdata;
        int i;
 
        if ((unsigned)slave_id >= SHDMA_SLAVE_NUMBER)
                return NULL;
 
-       for (i = 0; i < pdata->config_num; i++)
-               if (pdata->config[i].slave_id == slave_id)
-                       return pdata->config + i;
+       for (i = 0; i < pdata->slave_num; i++)
+               if (pdata->slave[i].slave_id == slave_id)
+                       return pdata->slave + i;
 
        return NULL;
 }
@@ -270,6 +290,8 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
        struct sh_desc *desc;
        struct sh_dmae_slave *param = chan->private;
 
+       pm_runtime_get_sync(sh_chan->dev);
+
        /*
         * This relies on the guarantee from dmaengine that alloc_chan_resources
         * never runs concurrently with itself or free_chan_resources.
@@ -288,9 +310,8 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
 
                dmae_set_dmars(sh_chan, cfg->mid_rid);
                dmae_set_chcr(sh_chan, cfg->chcr);
-       } else {
-               if ((sh_dmae_readl(sh_chan, CHCR) & 0x700) != 0x400)
-                       dmae_set_chcr(sh_chan, RS_DEFAULT);
+       } else if ((sh_dmae_readl(sh_chan, CHCR) & 0xf00) != 0x400) {
+               dmae_init(sh_chan);
        }
 
        spin_lock_bh(&sh_chan->desc_lock);
@@ -312,6 +333,9 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
        }
        spin_unlock_bh(&sh_chan->desc_lock);
 
+       if (!sh_chan->descs_allocated)
+               pm_runtime_put(sh_chan->dev);
+
        return sh_chan->descs_allocated;
 }
 
@@ -323,6 +347,7 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
        struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
        struct sh_desc *desc, *_desc;
        LIST_HEAD(list);
+       int descs = sh_chan->descs_allocated;
 
        dmae_halt(sh_chan);
 
@@ -343,6 +368,9 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan)
 
        spin_unlock_bh(&sh_chan->desc_lock);
 
+       if (descs > 0)
+               pm_runtime_put(sh_chan->dev);
+
        list_for_each_entry_safe(desc, _desc, &list, node)
                kfree(desc);
 }
@@ -559,6 +587,19 @@ static void sh_dmae_terminate_all(struct dma_chan *chan)
        if (!chan)
                return;
 
+       dmae_halt(sh_chan);
+
+       spin_lock_bh(&sh_chan->desc_lock);
+       if (!list_empty(&sh_chan->ld_queue)) {
+               /* Record partial transfer */
+               struct sh_desc *desc = list_entry(sh_chan->ld_queue.next,
+                                                 struct sh_desc, node);
+               desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
+                       sh_chan->xmit_shift;
+
+       }
+       spin_unlock_bh(&sh_chan->desc_lock);
+
        sh_dmae_chan_ld_cleanup(sh_chan, true);
 }
 
@@ -661,7 +702,7 @@ static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
 
 static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
 {
-       struct sh_desc *sd;
+       struct sh_desc *desc;
 
        spin_lock_bh(&sh_chan->desc_lock);
        /* DMA work check */
@@ -671,10 +712,13 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
        }
 
        /* Find the first not transferred desciptor */
-       list_for_each_entry(sd, &sh_chan->ld_queue, node)
-               if (sd->mark == DESC_SUBMITTED) {
+       list_for_each_entry(desc, &sh_chan->ld_queue, node)
+               if (desc->mark == DESC_SUBMITTED) {
+                       dev_dbg(sh_chan->dev, "Queue #%d to %d: %u@%x -> %x\n",
+                               desc->async_tx.cookie, sh_chan->id,
+                               desc->hw.tcr, desc->hw.sar, desc->hw.dar);
                        /* Get the ld start address from ld_queue */
-                       dmae_set_reg(sh_chan, &sd->hw);
+                       dmae_set_reg(sh_chan, &desc->hw);
                        dmae_start(sh_chan);
                        break;
                }
@@ -696,6 +740,7 @@ static enum dma_status sh_dmae_is_complete(struct dma_chan *chan,
        struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
        dma_cookie_t last_used;
        dma_cookie_t last_complete;
+       enum dma_status status;
 
        sh_dmae_chan_ld_cleanup(sh_chan, false);
 
@@ -709,7 +754,27 @@ static enum dma_status sh_dmae_is_complete(struct dma_chan *chan,
        if (used)
                *used = last_used;
 
-       return dma_async_is_complete(cookie, last_complete, last_used);
+       spin_lock_bh(&sh_chan->desc_lock);
+
+       status = dma_async_is_complete(cookie, last_complete, last_used);
+
+       /*
+        * If we don't find cookie on the queue, it has been aborted and we have
+        * to report error
+        */
+       if (status != DMA_SUCCESS) {
+               struct sh_desc *desc;
+               status = DMA_ERROR;
+               list_for_each_entry(desc, &sh_chan->ld_queue, node)
+                       if (desc->cookie == cookie) {
+                               status = DMA_IN_PROGRESS;
+                               break;
+                       }
+       }
+
+       spin_unlock_bh(&sh_chan->desc_lock);
+
+       return status;
 }
 
 static irqreturn_t sh_dmae_interrupt(int irq, void *data)
@@ -732,40 +797,32 @@ static irqreturn_t sh_dmae_interrupt(int irq, void *data)
 #if defined(CONFIG_CPU_SH4)
 static irqreturn_t sh_dmae_err(int irq, void *data)
 {
-       int err = 0;
        struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
+       int i;
 
-       /* IRQ Multi */
-       if (shdev->pdata.mode & SHDMA_MIX_IRQ) {
-               int __maybe_unused cnt = 0;
-               switch (irq) {
-#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ)
-               case DMTE6_IRQ:
-                       cnt++;
-#endif
-               case DMTE0_IRQ:
-                       if (dmaor_read_reg(cnt) & (DMAOR_NMIF | DMAOR_AE)) {
-                               disable_irq(irq);
-                               return IRQ_HANDLED;
+       /* halt the dma controller */
+       sh_dmae_ctl_stop(shdev);
+
+       /* We cannot detect, which channel caused the error, have to reset all */
+       for (i = 0; i < SH_DMAC_MAX_CHANNELS; i++) {
+               struct sh_dmae_chan *sh_chan = shdev->chan[i];
+               if (sh_chan) {
+                       struct sh_desc *desc;
+                       /* Stop the channel */
+                       dmae_halt(sh_chan);
+                       /* Complete all  */
+                       list_for_each_entry(desc, &sh_chan->ld_queue, node) {
+                               struct dma_async_tx_descriptor *tx = &desc->async_tx;
+                               desc->mark = DESC_IDLE;
+                               if (tx->callback)
+                                       tx->callback(tx->callback_param);
                        }
-               default:
-                       return IRQ_NONE;
+                       list_splice_init(&sh_chan->ld_queue, &sh_chan->ld_free);
                }
-       } else {
-               /* reset dma controller */
-               err = sh_dmae_rst(0);
-               if (err)
-                       return err;
-#ifdef SH_DMAC_BASE1
-               if (shdev->pdata.mode & SHDMA_DMAOR1) {
-                       err = sh_dmae_rst(1);
-                       if (err)
-                               return err;
-               }
-#endif
-               disable_irq(irq);
-               return IRQ_HANDLED;
        }
+       sh_dmae_rst(shdev);
+
+       return IRQ_HANDLED;
 }
 #endif
 
@@ -796,19 +853,12 @@ static void dmae_do_tasklet(unsigned long data)
        sh_dmae_chan_ld_cleanup(sh_chan, false);
 }
 
-static unsigned int get_dmae_irq(unsigned int id)
-{
-       unsigned int irq = 0;
-       if (id < ARRAY_SIZE(dmte_irq_map))
-               irq = dmte_irq_map[id];
-       return irq;
-}
-
-static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id)
+static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
+                                       int irq, unsigned long flags)
 {
        int err;
-       unsigned int irq = get_dmae_irq(id);
-       unsigned long irqflags = IRQF_DISABLED;
+       struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id];
+       struct platform_device *pdev = to_platform_device(shdev->common.dev);
        struct sh_dmae_chan *new_sh_chan;
 
        /* alloc channel */
@@ -819,8 +869,13 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id)
                return -ENOMEM;
        }
 
+       /* copy struct dma_device */
+       new_sh_chan->common.device = &shdev->common;
+
        new_sh_chan->dev = shdev->common.dev;
        new_sh_chan->id = id;
+       new_sh_chan->irq = irq;
+       new_sh_chan->base = shdev->chan_reg + chan_pdata->offset / sizeof(u32);
 
        /* Init DMA tasklet */
        tasklet_init(&new_sh_chan->tasklet, dmae_do_tasklet,
@@ -835,29 +890,20 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id)
        INIT_LIST_HEAD(&new_sh_chan->ld_queue);
        INIT_LIST_HEAD(&new_sh_chan->ld_free);
 
-       /* copy struct dma_device */
-       new_sh_chan->common.device = &shdev->common;
-
        /* Add the channel to DMA device channel list */
        list_add_tail(&new_sh_chan->common.device_node,
                        &shdev->common.channels);
        shdev->common.chancnt++;
 
-       if (shdev->pdata.mode & SHDMA_MIX_IRQ) {
-               irqflags = IRQF_SHARED;
-#if defined(DMTE6_IRQ)
-               if (irq >= DMTE6_IRQ)
-                       irq = DMTE6_IRQ;
-               else
-#endif
-                       irq = DMTE0_IRQ;
-       }
-
-       snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
-                "sh-dmae%d", new_sh_chan->id);
+       if (pdev->id >= 0)
+               snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
+                        "sh-dmae%d.%d", pdev->id, new_sh_chan->id);
+       else
+               snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
+                        "sh-dma%d", new_sh_chan->id);
 
        /* set up channel irq */
-       err = request_irq(irq, &sh_dmae_interrupt, irqflags,
+       err = request_irq(irq, &sh_dmae_interrupt, flags,
                          new_sh_chan->dev_id, new_sh_chan);
        if (err) {
                dev_err(shdev->common.dev, "DMA channel %d request_irq error "
@@ -881,12 +927,12 @@ static void sh_dmae_chan_remove(struct sh_dmae_device *shdev)
 
        for (i = shdev->common.chancnt - 1 ; i >= 0 ; i--) {
                if (shdev->chan[i]) {
-                       struct sh_dmae_chan *shchan = shdev->chan[i];
-                       if (!(shdev->pdata.mode & SHDMA_MIX_IRQ))
-                               free_irq(dmte_irq_map[i], shchan);
+                       struct sh_dmae_chan *sh_chan = shdev->chan[i];
 
-                       list_del(&shchan->common.device_node);
-                       kfree(shchan);
+                       free_irq(sh_chan->irq, sh_chan);
+
+                       list_del(&sh_chan->common.device_node);
+                       kfree(sh_chan);
                        shdev->chan[i] = NULL;
                }
        }
@@ -895,47 +941,84 @@ static void sh_dmae_chan_remove(struct sh_dmae_device *shdev)
 
 static int __init sh_dmae_probe(struct platform_device *pdev)
 {
-       int err = 0, cnt, ecnt;
-       unsigned long irqflags = IRQF_DISABLED;
-#if defined(CONFIG_CPU_SH4)
-       int eirq[] = { DMAE0_IRQ,
-#if defined(DMAE1_IRQ)
-                       DMAE1_IRQ
-#endif
-               };
-#endif
+       struct sh_dmae_pdata *pdata = pdev->dev.platform_data;
+       unsigned long irqflags = IRQF_DISABLED,
+               chan_flag[SH_DMAC_MAX_CHANNELS] = {};
+       int errirq, chan_irq[SH_DMAC_MAX_CHANNELS];
+       int err, i, irq_cnt = 0, irqres = 0;
        struct sh_dmae_device *shdev;
+       struct resource *chan, *dmars, *errirq_res, *chanirq_res;
 
        /* get platform data */
-       if (!pdev->dev.platform_data)
+       if (!pdata || !pdata->channel_num)
                return -ENODEV;
 
+       chan = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       /* DMARS area is optional, if absent, this controller cannot do slave DMA */
+       dmars = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       /*
+        * IRQ resources:
+        * 1. there always must be at least one IRQ IO-resource. On SH4 it is
+        *    the error IRQ, in which case it is the only IRQ in this resource:
+        *    start == end. If it is the only IRQ resource, all channels also
+        *    use the same IRQ.
+        * 2. DMA channel IRQ resources can be specified one per resource or in
+        *    ranges (start != end)
+        * 3. iff all events (channels and, optionally, error) on this
+        *    controller use the same IRQ, only one IRQ resource can be
+        *    specified, otherwise there must be one IRQ per channel, even if
+        *    some of them are equal
+        * 4. if all IRQs on this controller are equal or if some specific IRQs
+        *    specify IORESOURCE_IRQ_SHAREABLE in their resources, they will be
+        *    requested with the IRQF_SHARED flag
+        */
+       errirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!chan || !errirq_res)
+               return -ENODEV;
+
+       if (!request_mem_region(chan->start, resource_size(chan), pdev->name)) {
+               dev_err(&pdev->dev, "DMAC register region already claimed\n");
+               return -EBUSY;
+       }
+
+       if (dmars && !request_mem_region(dmars->start, resource_size(dmars), pdev->name)) {
+               dev_err(&pdev->dev, "DMAC DMARS region already claimed\n");
+               err = -EBUSY;
+               goto ermrdmars;
+       }
+
+       err = -ENOMEM;
        shdev = kzalloc(sizeof(struct sh_dmae_device), GFP_KERNEL);
        if (!shdev) {
-               dev_err(&pdev->dev, "No enough memory\n");
-               return -ENOMEM;
+               dev_err(&pdev->dev, "Not enough memory\n");
+               goto ealloc;
+       }
+
+       shdev->chan_reg = ioremap(chan->start, resource_size(chan));
+       if (!shdev->chan_reg)
+               goto emapchan;
+       if (dmars) {
+               shdev->dmars = ioremap(dmars->start, resource_size(dmars));
+               if (!shdev->dmars)
+                       goto emapdmars;
        }
 
        /* platform data */
-       memcpy(&shdev->pdata, pdev->dev.platform_data,
-                       sizeof(struct sh_dmae_pdata));
+       shdev->pdata = pdata;
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_get_sync(&pdev->dev);
 
        /* reset dma controller */
-       err = sh_dmae_rst(0);
+       err = sh_dmae_rst(shdev);
        if (err)
                goto rst_err;
 
-       /* SH7780/85/23 has DMAOR1 */
-       if (shdev->pdata.mode & SHDMA_DMAOR1) {
-               err = sh_dmae_rst(1);
-               if (err)
-                       goto rst_err;
-       }
-
        INIT_LIST_HEAD(&shdev->common.channels);
 
        dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask);
-       dma_cap_set(DMA_SLAVE, shdev->common.cap_mask);
+       if (dmars)
+               dma_cap_set(DMA_SLAVE, shdev->common.cap_mask);
 
        shdev->common.device_alloc_chan_resources
                = sh_dmae_alloc_chan_resources;
@@ -950,37 +1033,72 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 
        shdev->common.dev = &pdev->dev;
        /* Default transfer size of 32 bytes requires 32-byte alignment */
-       shdev->common.copy_align = 5;
+       shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
 
 #if defined(CONFIG_CPU_SH4)
-       /* Non Mix IRQ mode SH7722/SH7730 etc... */
-       if (shdev->pdata.mode & SHDMA_MIX_IRQ) {
+       chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+
+       if (!chanirq_res)
+               chanirq_res = errirq_res;
+       else
+               irqres++;
+
+       if (chanirq_res == errirq_res ||
+           (errirq_res->flags & IORESOURCE_BITS) == IORESOURCE_IRQ_SHAREABLE)
                irqflags = IRQF_SHARED;
-               eirq[0] = DMTE0_IRQ;
-#if defined(DMTE6_IRQ) && defined(DMAE1_IRQ)
-               eirq[1] = DMTE6_IRQ;
-#endif
+
+       errirq = errirq_res->start;
+
+       err = request_irq(errirq, sh_dmae_err, irqflags,
+                         "DMAC Address Error", shdev);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "DMA failed requesting irq #%d, error %d\n",
+                       errirq, err);
+               goto eirq_err;
        }
 
-       for (ecnt = 0 ; ecnt < ARRAY_SIZE(eirq); ecnt++) {
-               err = request_irq(eirq[ecnt], sh_dmae_err, irqflags,
-                                 "DMAC Address Error", shdev);
-               if (err) {
-                       dev_err(&pdev->dev, "DMA device request_irq"
-                               "error (irq %d) with return %d\n",
-                               eirq[ecnt], err);
-                       goto eirq_err;
+#else
+       chanirq_res = errirq_res;
+#endif /* CONFIG_CPU_SH4 */
+
+       if (chanirq_res->start == chanirq_res->end &&
+           !platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
+               /* Special case - all multiplexed */
+               for (; irq_cnt < pdata->channel_num; irq_cnt++) {
+                       chan_irq[irq_cnt] = chanirq_res->start;
+                       chan_flag[irq_cnt] = IRQF_SHARED;
                }
+       } else {
+               do {
+                       for (i = chanirq_res->start; i <= chanirq_res->end; i++) {
+                               if ((errirq_res->flags & IORESOURCE_BITS) ==
+                                   IORESOURCE_IRQ_SHAREABLE)
+                                       chan_flag[irq_cnt] = IRQF_SHARED;
+                               else
+                                       chan_flag[irq_cnt] = IRQF_DISABLED;
+                               dev_dbg(&pdev->dev,
+                                       "Found IRQ %d for channel %d\n",
+                                       i, irq_cnt);
+                               chan_irq[irq_cnt++] = i;
+                       }
+                       chanirq_res = platform_get_resource(pdev,
+                                               IORESOURCE_IRQ, ++irqres);
+               } while (irq_cnt < pdata->channel_num && chanirq_res);
        }
-#endif /* CONFIG_CPU_SH4 */
+
+       if (irq_cnt < pdata->channel_num)
+               goto eirqres;
 
        /* Create DMA Channel */
-       for (cnt = 0 ; cnt < MAX_DMA_CHANNELS ; cnt++) {
-               err = sh_dmae_chan_probe(shdev, cnt);
+       for (i = 0; i < pdata->channel_num; i++) {
+               err = sh_dmae_chan_probe(shdev, i, chan_irq[i], chan_flag[i]);
                if (err)
                        goto chan_probe_err;
        }
 
+       pm_runtime_put(&pdev->dev);
+
        platform_set_drvdata(pdev, shdev);
        dma_async_device_register(&shdev->common);
 
@@ -988,13 +1106,24 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 
 chan_probe_err:
        sh_dmae_chan_remove(shdev);
-
+eirqres:
+#if defined(CONFIG_CPU_SH4)
+       free_irq(errirq, shdev);
 eirq_err:
-       for (ecnt-- ; ecnt >= 0; ecnt--)
-               free_irq(eirq[ecnt], shdev);
-
+#endif
 rst_err:
+       pm_runtime_put(&pdev->dev);
+       if (dmars)
+               iounmap(shdev->dmars);
+emapdmars:
+       iounmap(shdev->chan_reg);
+emapchan:
        kfree(shdev);
+ealloc:
+       if (dmars)
+               release_mem_region(dmars->start, resource_size(dmars));
+ermrdmars:
+       release_mem_region(chan->start, resource_size(chan));
 
        return err;
 }
@@ -1002,36 +1131,39 @@ rst_err:
 static int __exit sh_dmae_remove(struct platform_device *pdev)
 {
        struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
+       struct resource *res;
+       int errirq = platform_get_irq(pdev, 0);
 
        dma_async_device_unregister(&shdev->common);
 
-       if (shdev->pdata.mode & SHDMA_MIX_IRQ) {
-               free_irq(DMTE0_IRQ, shdev);
-#if defined(DMTE6_IRQ)
-               free_irq(DMTE6_IRQ, shdev);
-#endif
-       }
+       if (errirq > 0)
+               free_irq(errirq, shdev);
 
        /* channel data remove */
        sh_dmae_chan_remove(shdev);
 
-       if (!(shdev->pdata.mode & SHDMA_MIX_IRQ)) {
-               free_irq(DMAE0_IRQ, shdev);
-#if defined(DMAE1_IRQ)
-               free_irq(DMAE1_IRQ, shdev);
-#endif
-       }
+       pm_runtime_disable(&pdev->dev);
+
+       if (shdev->dmars)
+               iounmap(shdev->dmars);
+       iounmap(shdev->chan_reg);
+
        kfree(shdev);
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res)
+               release_mem_region(res->start, resource_size(res));
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res)
+               release_mem_region(res->start, resource_size(res));
+
        return 0;
 }
 
 static void sh_dmae_shutdown(struct platform_device *pdev)
 {
        struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
-       sh_dmae_ctl_stop(0);
-       if (shdev->pdata.mode & SHDMA_DMAOR1)
-               sh_dmae_ctl_stop(1);
+       sh_dmae_ctl_stop(shdev);
 }
 
 static struct platform_driver sh_dmae_driver = {
index 7e227f3c87c456b476d6be798419d9046c6576d3..153609a1e96c55092f2eb99911552eee7b08e802 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/list.h>
 
-#define SH_DMA_TCR_MAX 0x00FFFFFF      /* 16MB */
-
-struct sh_dmae_regs {
-       u32 sar; /* SAR / source address */
-       u32 dar; /* DAR / destination address */
-       u32 tcr; /* TCR / transfer count */
-};
+#include <asm/dmaengine.h>
 
-struct sh_desc {
-       struct sh_dmae_regs hw;
-       struct list_head node;
-       struct dma_async_tx_descriptor async_tx;
-       enum dma_data_direction direction;
-       dma_cookie_t cookie;
-       int chunks;
-       int mark;
-};
+#define SH_DMA_TCR_MAX 0x00FFFFFF      /* 16MB */
 
 struct device;
 
@@ -47,14 +33,18 @@ struct sh_dmae_chan {
        struct tasklet_struct tasklet;  /* Tasklet */
        int descs_allocated;            /* desc count */
        int xmit_shift;                 /* log_2(bytes_per_xfer) */
+       int irq;
        int id;                         /* Raw id of this channel */
+       u32 __iomem *base;
        char dev_id[16];                /* unique name per DMAC of channel */
 };
 
 struct sh_dmae_device {
        struct dma_device common;
-       struct sh_dmae_chan *chan[MAX_DMA_CHANNELS];
-       struct sh_dmae_pdata pdata;
+       struct sh_dmae_chan *chan[SH_DMAC_MAX_CHANNELS];
+       struct sh_dmae_pdata *pdata;
+       u32 __iomem *chan_reg;
+       u16 __iomem *dmars;
 };
 
 #define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common)
index 66958b3f10b426c3fcee2135a61087c4b50241bd..806c77bfd434e7b6459197627265d8367340e4a2 100644 (file)
@@ -39,10 +39,10 @@ static unsigned int enable_dev_count;
 static int disable_dev[EISA_MAX_FORCED_DEV];
 static unsigned int disable_dev_count;
 
-static int is_forced_dev (int *forced_tab,
-                         int forced_count,
-                         struct eisa_root_device *root,
-                         struct eisa_device *edev)
+static int is_forced_dev(int *forced_tab,
+                        int forced_count,
+                        struct eisa_root_device *root,
+                        struct eisa_device *edev)
 {
        int i, x;
 
@@ -55,21 +55,21 @@ static int is_forced_dev (int *forced_tab,
        return 0;
 }
 
-static void __init eisa_name_device (struct eisa_device *edev)
+static void __init eisa_name_device(struct eisa_device *edev)
 {
 #ifdef CONFIG_EISA_NAMES
        int i;
        for (i = 0; i < EISA_INFOS; i++) {
-               if (!strcmp (edev->id.sig, eisa_table[i].id.sig)) {
-                       strlcpy (edev->pretty_name,
-                                eisa_table[i].name,
-                                sizeof(edev->pretty_name));
+               if (!strcmp(edev->id.sig, eisa_table[i].id.sig)) {
+                       strlcpy(edev->pretty_name,
+                               eisa_table[i].name,
+                               sizeof(edev->pretty_name));
                        return;
                }
        }
 
        /* No name was found */
-       sprintf (edev->pretty_name, "EISA device %.7s", edev->id.sig);
+       sprintf(edev->pretty_name, "EISA device %.7s", edev->id.sig);
 #endif
 }
 
@@ -91,7 +91,7 @@ static char __init *decode_eisa_sig(unsigned long addr)
                 */
                outb(0x80 + i, addr);
 #endif
-               sig[i] = inb (addr + i);
+               sig[i] = inb(addr + i);
 
                if (!i && (sig[0] & 0x80))
                        return NULL;
@@ -106,17 +106,17 @@ static char __init *decode_eisa_sig(unsigned long addr)
         return sig_str;
 }
 
-static int eisa_bus_match (struct device *dev, struct device_driver *drv)
+static int eisa_bus_match(struct device *dev, struct device_driver *drv)
 {
-       struct eisa_device *edev = to_eisa_device (dev);
-       struct eisa_driver *edrv = to_eisa_driver (drv);
+       struct eisa_device *edev = to_eisa_device(dev);
+       struct eisa_driver *edrv = to_eisa_driver(drv);
        const struct eisa_device_id *eids = edrv->id_table;
 
        if (!eids)
                return 0;
 
-       while (strlen (eids->sig)) {
-               if (!strcmp (eids->sig, edev->id.sig) &&
+       while (strlen(eids->sig)) {
+               if (!strcmp(eids->sig, edev->id.sig) &&
                    edev->state & EISA_CONFIG_ENABLED) {
                        edev->id.driver_data = eids->driver_data;
                        return 1;
@@ -141,61 +141,71 @@ struct bus_type eisa_bus_type = {
        .match = eisa_bus_match,
        .uevent = eisa_bus_uevent,
 };
+EXPORT_SYMBOL(eisa_bus_type);
 
-int eisa_driver_register (struct eisa_driver *edrv)
+int eisa_driver_register(struct eisa_driver *edrv)
 {
        edrv->driver.bus = &eisa_bus_type;
-       return driver_register (&edrv->driver);
+       return driver_register(&edrv->driver);
 }
+EXPORT_SYMBOL(eisa_driver_register);
 
-void eisa_driver_unregister (struct eisa_driver *edrv)
+void eisa_driver_unregister(struct eisa_driver *edrv)
 {
-       driver_unregister (&edrv->driver);
+       driver_unregister(&edrv->driver);
 }
+EXPORT_SYMBOL(eisa_driver_unregister);
 
-static ssize_t eisa_show_sig (struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t eisa_show_sig(struct device *dev, struct device_attribute *attr,
+                            char *buf)
 {
-        struct eisa_device *edev = to_eisa_device (dev);
-        return sprintf (buf,"%s\n", edev->id.sig);
+       struct eisa_device *edev = to_eisa_device(dev);
+       return sprintf(buf, "%s\n", edev->id.sig);
 }
 
 static DEVICE_ATTR(signature, S_IRUGO, eisa_show_sig, NULL);
 
-static ssize_t eisa_show_state (struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t eisa_show_state(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
 {
-        struct eisa_device *edev = to_eisa_device (dev);
-        return sprintf (buf,"%d\n", edev->state & EISA_CONFIG_ENABLED);
+       struct eisa_device *edev = to_eisa_device(dev);
+       return sprintf(buf, "%d\n", edev->state & EISA_CONFIG_ENABLED);
 }
 
 static DEVICE_ATTR(enabled, S_IRUGO, eisa_show_state, NULL);
 
-static ssize_t eisa_show_modalias (struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t eisa_show_modalias(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
 {
-        struct eisa_device *edev = to_eisa_device (dev);
-        return sprintf (buf, EISA_DEVICE_MODALIAS_FMT "\n", edev->id.sig);
+       struct eisa_device *edev = to_eisa_device(dev);
+       return sprintf(buf, EISA_DEVICE_MODALIAS_FMT "\n", edev->id.sig);
 }
 
 static DEVICE_ATTR(modalias, S_IRUGO, eisa_show_modalias, NULL);
 
-static int __init eisa_init_device (struct eisa_root_device *root,
-                                   struct eisa_device *edev,
-                                   int slot)
+static int __init eisa_init_device(struct eisa_root_device *root,
+                                  struct eisa_device *edev,
+                                  int slot)
 {
        char *sig;
-        unsigned long sig_addr;
+       unsigned long sig_addr;
        int i;
 
-       sig_addr = SLOT_ADDRESS (root, slot) + EISA_VENDOR_ID_OFFSET;
+       sig_addr = SLOT_ADDRESS(root, slot) + EISA_VENDOR_ID_OFFSET;
 
-       if (!(sig = decode_eisa_sig (sig_addr)))
+       sig = decode_eisa_sig(sig_addr);
+       if (!sig)
                return -1;      /* No EISA device here */
        
-       memcpy (edev->id.sig, sig, EISA_SIG_LEN);
+       memcpy(edev->id.sig, sig, EISA_SIG_LEN);
        edev->slot = slot;
-       edev->state = inb (SLOT_ADDRESS (root, slot) + EISA_CONFIG_OFFSET) & EISA_CONFIG_ENABLED;
-       edev->base_addr = SLOT_ADDRESS (root, slot);
+       edev->state = inb(SLOT_ADDRESS(root, slot) + EISA_CONFIG_OFFSET)
+                     & EISA_CONFIG_ENABLED;
+       edev->base_addr = SLOT_ADDRESS(root, slot);
        edev->dma_mask = root->dma_mask; /* Default DMA mask */
-       eisa_name_device (edev);
+       eisa_name_device(edev);
        edev->dev.parent = root->dev;
        edev->dev.bus = &eisa_bus_type;
        edev->dev.dma_mask = &edev->dma_mask;
@@ -210,42 +220,45 @@ static int __init eisa_init_device (struct eisa_root_device *root,
 #endif
        }
 
-       if (is_forced_dev (enable_dev, enable_dev_count, root, edev))
+       if (is_forced_dev(enable_dev, enable_dev_count, root, edev))
                edev->state = EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED;
        
-       if (is_forced_dev (disable_dev, disable_dev_count, root, edev))
+       if (is_forced_dev(disable_dev, disable_dev_count, root, edev))
                edev->state = EISA_CONFIG_FORCED;
 
        return 0;
 }
 
-static int __init eisa_register_device (struct eisa_device *edev)
+static int __init eisa_register_device(struct eisa_device *edev)
 {
-       int rc = device_register (&edev->dev);
+       int rc = device_register(&edev->dev);
        if (rc)
                return rc;
 
-       rc = device_create_file (&edev->dev, &dev_attr_signature);
-       if (rc) goto err_devreg;
-       rc = device_create_file (&edev->dev, &dev_attr_enabled);
-       if (rc) goto err_sig;
-       rc = device_create_file (&edev->dev, &dev_attr_modalias);
-       if (rc) goto err_enab;
+       rc = device_create_file(&edev->dev, &dev_attr_signature);
+       if (rc)
+               goto err_devreg;
+       rc = device_create_file(&edev->dev, &dev_attr_enabled);
+       if (rc)
+               goto err_sig;
+       rc = device_create_file(&edev->dev, &dev_attr_modalias);
+       if (rc)
+               goto err_enab;
 
        return 0;
 
 err_enab:
-       device_remove_file (&edev->dev, &dev_attr_enabled);
+       device_remove_file(&edev->dev, &dev_attr_enabled);
 err_sig:
-       device_remove_file (&edev->dev, &dev_attr_signature);
+       device_remove_file(&edev->dev, &dev_attr_signature);
 err_devreg:
        device_unregister(&edev->dev);
        return rc;
 }
 
-static int __init eisa_request_resources (struct eisa_root_device *root,
-                                         struct eisa_device *edev,
-                                         int slot)
+static int __init eisa_request_resources(struct eisa_root_device *root,
+                                        struct eisa_device *edev,
+                                        int slot)
 {
        int i;
 
@@ -263,17 +276,19 @@ static int __init eisa_request_resources (struct eisa_root_device *root,
                
                if (slot) {
                        edev->res[i].name  = NULL;
-                       edev->res[i].start = SLOT_ADDRESS (root, slot) + (i * 0x400);
+                       edev->res[i].start = SLOT_ADDRESS(root, slot)
+                                            + (i * 0x400);
                        edev->res[i].end   = edev->res[i].start + 0xff;
                        edev->res[i].flags = IORESOURCE_IO;
                } else {
                        edev->res[i].name  = NULL;
-                       edev->res[i].start = SLOT_ADDRESS (root, slot) + EISA_VENDOR_ID_OFFSET;
+                       edev->res[i].start = SLOT_ADDRESS(root, slot)
+                                            + EISA_VENDOR_ID_OFFSET;
                        edev->res[i].end   = edev->res[i].start + 3;
                        edev->res[i].flags = IORESOURCE_BUSY;
                }
 
-               if (request_resource (root->res, &edev->res[i]))
+               if (request_resource(root->res, &edev->res[i]))
                        goto failed;
        }
 
@@ -281,99 +296,100 @@ static int __init eisa_request_resources (struct eisa_root_device *root,
        
  failed:
        while (--i >= 0)
-               release_resource (&edev->res[i]);
+               release_resource(&edev->res[i]);
 
        return -1;
 }
 
-static void __init eisa_release_resources (struct eisa_device *edev)
+static void __init eisa_release_resources(struct eisa_device *edev)
 {
        int i;
 
        for (i = 0; i < EISA_MAX_RESOURCES; i++)
                if (edev->res[i].start || edev->res[i].end)
-                       release_resource (&edev->res[i]);
+                       release_resource(&edev->res[i]);
 }
 
-static int __init eisa_probe (struct eisa_root_device *root)
+static int __init eisa_probe(struct eisa_root_device *root)
 {
         int i, c;
        struct eisa_device *edev;
 
-        printk (KERN_INFO "EISA: Probing bus %d at %s\n",
-               root->bus_nr, dev_name(root->dev));
+       printk(KERN_INFO "EISA: Probing bus %d at %s\n",
+              root->bus_nr, dev_name(root->dev));
 
        /* First try to get hold of slot 0. If there is no device
         * here, simply fail, unless root->force_probe is set. */
        
-       if (!(edev = kzalloc (sizeof (*edev), GFP_KERNEL))) {
-               printk (KERN_ERR "EISA: Couldn't allocate mainboard slot\n");
+       edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+       if (!edev) {
+               printk(KERN_ERR "EISA: Couldn't allocate mainboard slot\n");
                return -ENOMEM;
        }
                
-       if (eisa_request_resources (root, edev, 0)) {
-               printk (KERN_WARNING \
-                       "EISA: Cannot allocate resource for mainboard\n");
-               kfree (edev);
+       if (eisa_request_resources(root, edev, 0)) {
+               printk(KERN_WARNING \
+                      "EISA: Cannot allocate resource for mainboard\n");
+               kfree(edev);
                if (!root->force_probe)
                        return -EBUSY;
                goto force_probe;
        }
 
-       if (eisa_init_device (root, edev, 0)) {
-               eisa_release_resources (edev);
-               kfree (edev);
+       if (eisa_init_device(root, edev, 0)) {
+               eisa_release_resources(edev);
+               kfree(edev);
                if (!root->force_probe)
                        return -ENODEV;
                goto force_probe;
        }
 
-       printk (KERN_INFO "EISA: Mainboard %s detected.\n", edev->id.sig);
+       printk(KERN_INFO "EISA: Mainboard %s detected.\n", edev->id.sig);
 
-       if (eisa_register_device (edev)) {
-               printk (KERN_ERR "EISA: Failed to register %s\n",
-                       edev->id.sig);
-               eisa_release_resources (edev);
-               kfree (edev);
+       if (eisa_register_device(edev)) {
+               printk(KERN_ERR "EISA: Failed to register %s\n",
+                      edev->id.sig);
+               eisa_release_resources(edev);
+               kfree(edev);
        }
        
  force_probe:
        
         for (c = 0, i = 1; i <= root->slots; i++) {
-               if (!(edev = kzalloc (sizeof (*edev), GFP_KERNEL))) {
-                       printk (KERN_ERR "EISA: Out of memory for slot %d\n",
-                               i);
+               edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+               if (!edev) {
+                       printk(KERN_ERR "EISA: Out of memory for slot %d\n", i);
                        continue;
                }
 
-               if (eisa_request_resources (root, edev, i)) {
-                       printk (KERN_WARNING \
-                               "Cannot allocate resource for EISA slot %d\n",
-                               i);
-                       kfree (edev);
+               if (eisa_request_resources(root, edev, i)) {
+                       printk(KERN_WARNING \
+                              "Cannot allocate resource for EISA slot %d\n",
+                              i);
+                       kfree(edev);
                        continue;
                }
 
-                if (eisa_init_device (root, edev, i)) {
-                       eisa_release_resources (edev);
-                       kfree (edev);
+               if (eisa_init_device(root, edev, i)) {
+                       eisa_release_resources(edev);
+                       kfree(edev);
                        continue;
                }
                
-               printk (KERN_INFO "EISA: slot %d : %s detected",
-                       i, edev->id.sig);
+               printk(KERN_INFO "EISA: slot %d : %s detected",
+                      i, edev->id.sig);
                        
                switch (edev->state) {
                case EISA_CONFIG_ENABLED | EISA_CONFIG_FORCED:
-                       printk (" (forced enabled)");
+                       printk(" (forced enabled)");
                        break;
 
                case EISA_CONFIG_FORCED:
-                       printk (" (forced disabled)");
+                       printk(" (forced disabled)");
                        break;
 
                case 0:
-                       printk (" (disabled)");
+                       printk(" (disabled)");
                        break;
                }
                        
@@ -381,15 +397,15 @@ static int __init eisa_probe (struct eisa_root_device *root)
 
                c++;
 
-               if (eisa_register_device (edev)) {
-                       printk (KERN_ERR "EISA: Failed to register %s\n",
-                               edev->id.sig);
-                       eisa_release_resources (edev);
-                       kfree (edev);
+               if (eisa_register_device(edev)) {
+                       printk(KERN_ERR "EISA: Failed to register %s\n",
+                              edev->id.sig);
+                       eisa_release_resources(edev);
+                       kfree(edev);
                }
         }
 
-        printk (KERN_INFO "EISA: Detected %d card%s.\n", c, c == 1 ? "" : "s");
+       printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c == 1 ? "" : "s");
 
        return 0;
 }
@@ -403,7 +419,7 @@ static struct resource eisa_root_res = {
 
 static int eisa_bus_count;
 
-int __init eisa_root_register (struct eisa_root_device *root)
+int __init eisa_root_register(struct eisa_root_device *root)
 {
        int err;
 
@@ -417,35 +433,35 @@ int __init eisa_root_register (struct eisa_root_device *root)
        root->eisa_root_res.end   = root->res->end;
        root->eisa_root_res.flags = IORESOURCE_BUSY;
 
-       if ((err = request_resource (&eisa_root_res, &root->eisa_root_res)))
+       err = request_resource(&eisa_root_res, &root->eisa_root_res);
+       if (err)
                return err;
        
        root->bus_nr = eisa_bus_count++;
 
-       if ((err = eisa_probe (root)))
-               release_resource (&root->eisa_root_res);
+       err = eisa_probe(root);
+       if (err)
+               release_resource(&root->eisa_root_res);
 
        return err;
 }
 
-static int __init eisa_init (void)
+static int __init eisa_init(void)
 {
        int r;
        
-       if ((r = bus_register (&eisa_bus_type)))
+       r = bus_register(&eisa_bus_type);
+       if (r)
                return r;
 
-       printk (KERN_INFO "EISA bus registered\n");
+       printk(KERN_INFO "EISA bus registered\n");
        return 0;
 }
 
 module_param_array(enable_dev, int, &enable_dev_count, 0444);
 module_param_array(disable_dev, int, &disable_dev_count, 0444);
 
-postcore_initcall (eisa_init);
+postcore_initcall(eisa_init);
 
 int EISA_bus;          /* for legacy drivers */
-EXPORT_SYMBOL (EISA_bus);
-EXPORT_SYMBOL (eisa_bus_type);
-EXPORT_SYMBOL (eisa_driver_register);
-EXPORT_SYMBOL (eisa_driver_unregister);
+EXPORT_SYMBOL(EISA_bus);
index 56f9234781faadc4cbb38813ffefa8d77b781ebe..20f645743ead18634cb928a530c3ed18ce4ff8fa 100644 (file)
@@ -122,29 +122,53 @@ static int firmware_map_add_entry(u64 start, u64 end,
        return 0;
 }
 
+/*
+ * Add memmap entry on sysfs
+ */
+static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
+{
+       static int map_entries_nr;
+       static struct kset *mmap_kset;
+
+       if (!mmap_kset) {
+               mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);
+               if (!mmap_kset)
+                       return -ENOMEM;
+       }
+
+       entry->kobj.kset = mmap_kset;
+       if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++))
+               kobject_put(&entry->kobj);
+
+       return 0;
+}
+
 /**
- * firmware_map_add() - Adds a firmware mapping entry.
+ * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do
+ * memory hotplug.
  * @start: Start of the memory range.
  * @end:   End of the memory range (inclusive).
  * @type:  Type of the memory range.
  *
- * This function uses kmalloc() for memory
- * allocation. Use firmware_map_add_early() if you want to use the bootmem
- * allocator.
- *
- * That function must be called before late_initcall.
+ * Adds a firmware mapping entry. This function is for memory hotplug, it is
+ * similar to function firmware_map_add_early(). The only difference is that
+ * it will create the syfs entry dynamically.
  *
  * Returns 0 on success, or -ENOMEM if no memory could be allocated.
  **/
-int firmware_map_add(u64 start, u64 end, const char *type)
+int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
 {
        struct firmware_map_entry *entry;
 
-       entry = kmalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
+       entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
        if (!entry)
                return -ENOMEM;
 
-       return firmware_map_add_entry(start, end, type, entry);
+       firmware_map_add_entry(start, end, type, entry);
+       /* create the memmap entry */
+       add_sysfs_fw_map_entry(entry);
+
+       return 0;
 }
 
 /**
@@ -154,7 +178,7 @@ int firmware_map_add(u64 start, u64 end, const char *type)
  * @type:  Type of the memory range.
  *
  * Adds a firmware mapping entry. This function uses the bootmem allocator
- * for memory allocation. Use firmware_map_add() if you want to use kmalloc().
+ * for memory allocation.
  *
  * That function must be called before late_initcall.
  *
@@ -214,19 +238,10 @@ static ssize_t memmap_attr_show(struct kobject *kobj,
  */
 static int __init memmap_init(void)
 {
-       int i = 0;
        struct firmware_map_entry *entry;
-       struct kset *memmap_kset;
-
-       memmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);
-       if (WARN_ON(!memmap_kset))
-               return -ENOMEM;
 
-       list_for_each_entry(entry, &map_entries, list) {
-               entry->kobj.kset = memmap_kset;
-               if (kobject_add(&entry->kobj, NULL, "%d", i++))
-                       kobject_put(&entry->kobj);
-       }
+       list_for_each_entry(entry, &map_entries, list)
+               add_sysfs_fw_map_entry(entry);
 
        return 0;
 }
index 1f1d88ae68d6af2659d4fbfef2f8cad589d0d349..a4cdbd51b1c66c13ea30bdf1b2833459a065aac8 100644 (file)
@@ -65,8 +65,17 @@ config GPIO_SYSFS
 
 # put expanders in the right section, in alphabetical order
 
+config GPIO_MAX730X
+       tristate
+
 comment "Memory mapped GPIO expanders:"
 
+config GPIO_IT8761E
+       tristate "IT8761E GPIO support"
+       depends on GPIOLIB
+       help
+         Say yes here to support GPIO functionality of IT8761E super I/O chip.
+
 config GPIO_PL061
        bool "PrimeCell PL061 GPIO support"
        depends on ARM_AMBA
@@ -87,6 +96,13 @@ config GPIO_VR41XX
 
 comment "I2C GPIO expanders:"
 
+config GPIO_MAX7300
+       tristate "Maxim MAX7300 GPIO expander"
+       depends on I2C
+       select GPIO_MAX730X
+       help
+         GPIO driver for Maxim MAX7301 I2C-based GPIO expander.
+
 config GPIO_MAX732X
        tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
        depends on I2C
@@ -124,6 +140,13 @@ config GPIO_PCA953X
          This driver can also be built as a module.  If so, the module
          will be called pca953x.
 
+config GPIO_PCA953X_IRQ
+       bool "Interrupt controller support for PCA953x"
+       depends on GPIO_PCA953X=y
+       help
+         Say yes here to enable the pca953x to be used as an interrupt
+         controller. It requires the driver to be built in the kernel.
+
 config GPIO_PCF857X
        tristate "PCF857x, PCA{85,96}7x, and MAX732[89] I2C GPIO expanders"
        depends on I2C
@@ -226,8 +249,9 @@ comment "SPI GPIO expanders:"
 config GPIO_MAX7301
        tristate "Maxim MAX7301 GPIO expander"
        depends on SPI_MASTER
+       select GPIO_MAX730X
        help
-         gpio driver for Maxim MAX7301 SPI GPIO expander.
+         GPIO driver for Maxim MAX7301 SPI-based GPIO expander.
 
 config GPIO_MCP23S08
        tristate "Microchip MCP23S08 I/O expander"
index 48687238edb13dffe015fbeef2d1f8eefbf66711..128abf8a98da811d7d1f372fdc8a78b81a60b162 100644 (file)
@@ -7,6 +7,8 @@ obj-$(CONFIG_GPIOLIB)           += gpiolib.o
 obj-$(CONFIG_GPIO_ADP5520)     += adp5520-gpio.o
 obj-$(CONFIG_GPIO_ADP5588)     += adp5588-gpio.o
 obj-$(CONFIG_GPIO_LANGWELL)    += langwell_gpio.o
+obj-$(CONFIG_GPIO_MAX730X)     += max730x.o
+obj-$(CONFIG_GPIO_MAX7300)     += max7300.o
 obj-$(CONFIG_GPIO_MAX7301)     += max7301.o
 obj-$(CONFIG_GPIO_MAX732X)     += max732x.o
 obj-$(CONFIG_GPIO_MC33880)     += mc33880.o
@@ -20,5 +22,6 @@ obj-$(CONFIG_GPIO_UCB1400)    += ucb1400_gpio.o
 obj-$(CONFIG_GPIO_XILINX)      += xilinx_gpio.o
 obj-$(CONFIG_GPIO_CS5535)      += cs5535-gpio.o
 obj-$(CONFIG_GPIO_BT8XX)       += bt8xxgpio.o
+obj-$(CONFIG_GPIO_IT8761E)     += it8761e_gpio.o
 obj-$(CONFIG_GPIO_VR41XX)      += vr41xx_giu.o
 obj-$(CONFIG_GPIO_WM831X)      += wm831x-gpio.o
index 0fdbe94f24a3a0f320266d6505a3062e1aa5f6cd..0c3c498f2260632124ecac5ffcc2f5f81773e0d2 100644 (file)
@@ -154,7 +154,7 @@ static int chip_gpio_request(struct gpio_chip *c, unsigned offset)
 
 static int chip_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
-       return cs5535_gpio_isset(offset, GPIO_OUTPUT_VAL);
+       return cs5535_gpio_isset(offset, GPIO_READ_BACK);
 }
 
 static void chip_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
@@ -172,6 +172,7 @@ static int chip_direction_input(struct gpio_chip *c, unsigned offset)
 
        spin_lock_irqsave(&chip->lock, flags);
        __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
+       __cs5535_gpio_clear(chip, offset, GPIO_OUTPUT_ENABLE);
        spin_unlock_irqrestore(&chip->lock, flags);
 
        return 0;
@@ -184,6 +185,7 @@ static int chip_direction_output(struct gpio_chip *c, unsigned offset, int val)
 
        spin_lock_irqsave(&chip->lock, flags);
 
+       __cs5535_gpio_set(chip, offset, GPIO_INPUT_ENABLE);
        __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_ENABLE);
        if (val)
                __cs5535_gpio_set(chip, offset, GPIO_OUTPUT_VAL);
index 350842ad3632abc46469cd9c6835488a03b771db..9006fdb26fea2b01abc324c0f64b6ad9883eb7fa 100644 (file)
@@ -1237,6 +1237,64 @@ void gpio_free(unsigned gpio)
 }
 EXPORT_SYMBOL_GPL(gpio_free);
 
+/**
+ * gpio_request_one - request a single GPIO with initial configuration
+ * @gpio:      the GPIO number
+ * @flags:     GPIO configuration as specified by GPIOF_*
+ * @label:     a literal description string of this GPIO
+ */
+int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
+{
+       int err;
+
+       err = gpio_request(gpio, label);
+       if (err)
+               return err;
+
+       if (flags & GPIOF_DIR_IN)
+               err = gpio_direction_input(gpio);
+       else
+               err = gpio_direction_output(gpio,
+                               (flags & GPIOF_INIT_HIGH) ? 1 : 0);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(gpio_request_one);
+
+/**
+ * gpio_request_array - request multiple GPIOs in a single call
+ * @array:     array of the 'struct gpio'
+ * @num:       how many GPIOs in the array
+ */
+int gpio_request_array(struct gpio *array, size_t num)
+{
+       int i, err;
+
+       for (i = 0; i < num; i++, array++) {
+               err = gpio_request_one(array->gpio, array->flags, array->label);
+               if (err)
+                       goto err_free;
+       }
+       return 0;
+
+err_free:
+       while (i--)
+               gpio_free((--array)->gpio);
+       return err;
+}
+EXPORT_SYMBOL_GPL(gpio_request_array);
+
+/**
+ * gpio_free_array - release multiple GPIOs in a single call
+ * @array:     array of the 'struct gpio'
+ * @num:       how many GPIOs in the array
+ */
+void gpio_free_array(struct gpio *array, size_t num)
+{
+       while (num--)
+               gpio_free((array++)->gpio);
+}
+EXPORT_SYMBOL_GPL(gpio_free_array);
 
 /**
  * gpiochip_is_requested - return string iff signal was requested
diff --git a/drivers/gpio/it8761e_gpio.c b/drivers/gpio/it8761e_gpio.c
new file mode 100644 (file)
index 0000000..753219c
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  it8761_gpio.c - GPIO interface for IT8761E Super I/O chip
+ *
+ *  Author: Denis Turischev <denis@compulab.co.il>
+ *
+ *  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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+
+#include <linux/gpio.h>
+
+#define SIO_CHIP_ID            0x8761
+#define CHIP_ID_HIGH_BYTE      0x20
+#define CHIP_ID_LOW_BYTE       0x21
+
+static u8 ports[2] = { 0x2e, 0x4e };
+static u8 port;
+
+static DEFINE_SPINLOCK(sio_lock);
+
+#define GPIO_NAME              "it8761-gpio"
+#define GPIO_BA_HIGH_BYTE      0x60
+#define GPIO_BA_LOW_BYTE       0x61
+#define GPIO_IOSIZE            4
+#define GPIO1X_IO              0xf0
+#define GPIO2X_IO              0xf1
+
+static u16 gpio_ba;
+
+static u8 read_reg(u8 addr, u8 port)
+{
+       outb(addr, port);
+       return inb(port + 1);
+}
+
+static void write_reg(u8 data, u8 addr, u8 port)
+{
+       outb(addr, port);
+       outb(data, port + 1);
+}
+
+static void enter_conf_mode(u8 port)
+{
+       outb(0x87, port);
+       outb(0x61, port);
+       outb(0x55, port);
+       outb((port == 0x2e) ? 0x55 : 0xaa, port);
+}
+
+static void exit_conf_mode(u8 port)
+{
+       outb(0x2, port);
+       outb(0x2, port + 1);
+}
+
+static void enter_gpio_mode(u8 port)
+{
+       write_reg(0x2, 0x7, port);
+}
+
+static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+       u16 reg;
+       u8 bit;
+
+       bit = gpio_num % 7;
+       reg = (gpio_num >= 7) ? gpio_ba + 1 : gpio_ba;
+
+       return !!(inb(reg) & (1 << bit));
+}
+
+static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+{
+       u8 curr_dirs;
+       u8 io_reg, bit;
+
+       bit = gpio_num % 7;
+       io_reg = (gpio_num >= 7) ? GPIO2X_IO : GPIO1X_IO;
+
+       spin_lock(&sio_lock);
+
+       enter_conf_mode(port);
+       enter_gpio_mode(port);
+
+       curr_dirs = read_reg(io_reg, port);
+
+       if (curr_dirs & (1 << bit))
+               write_reg(curr_dirs & ~(1 << bit), io_reg, port);
+
+       exit_conf_mode(port);
+
+       spin_unlock(&sio_lock);
+       return 0;
+}
+
+static void it8761e_gpio_set(struct gpio_chip *gc,
+                               unsigned gpio_num, int val)
+{
+       u8 curr_vals, bit;
+       u16 reg;
+
+       bit = gpio_num % 7;
+       reg = (gpio_num >= 7) ? gpio_ba + 1 : gpio_ba;
+
+       spin_lock(&sio_lock);
+
+       curr_vals = inb(reg);
+       if (val)
+               outb(curr_vals | (1 << bit) , reg);
+       else
+               outb(curr_vals & ~(1 << bit), reg);
+
+       spin_unlock(&sio_lock);
+}
+
+static int it8761e_gpio_direction_out(struct gpio_chip *gc,
+                                       unsigned gpio_num, int val)
+{
+       u8 curr_dirs, io_reg, bit;
+
+       bit = gpio_num % 7;
+       io_reg = (gpio_num >= 7) ? GPIO2X_IO : GPIO1X_IO;
+
+       it8761e_gpio_set(gc, gpio_num, val);
+
+       spin_lock(&sio_lock);
+
+       enter_conf_mode(port);
+       enter_gpio_mode(port);
+
+       curr_dirs = read_reg(io_reg, port);
+
+       if (!(curr_dirs & (1 << bit)))
+               write_reg(curr_dirs | (1 << bit), io_reg, port);
+
+       exit_conf_mode(port);
+
+       spin_unlock(&sio_lock);
+       return 0;
+}
+
+static struct gpio_chip it8761e_gpio_chip = {
+       .label                  = GPIO_NAME,
+       .owner                  = THIS_MODULE,
+       .get                    = it8761e_gpio_get,
+       .direction_input        = it8761e_gpio_direction_in,
+       .set                    = it8761e_gpio_set,
+       .direction_output       = it8761e_gpio_direction_out,
+};
+
+static int __init it8761e_gpio_init(void)
+{
+       int i, id, err;
+
+       /* chip and port detection */
+       for (i = 0; i < ARRAY_SIZE(ports); i++) {
+               spin_lock(&sio_lock);
+               enter_conf_mode(ports[i]);
+
+               id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) +
+                               read_reg(CHIP_ID_LOW_BYTE, ports[i]);
+
+               exit_conf_mode(ports[i]);
+               spin_unlock(&sio_lock);
+
+               if (id == SIO_CHIP_ID) {
+                       port = ports[i];
+                       break;
+               }
+       }
+
+       if (!port)
+               return -ENODEV;
+
+       /* fetch GPIO base address */
+       enter_conf_mode(port);
+       enter_gpio_mode(port);
+       gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) +
+                               read_reg(GPIO_BA_LOW_BYTE, port);
+       exit_conf_mode(port);
+
+       if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME))
+               return -EBUSY;
+
+       it8761e_gpio_chip.base = -1;
+       it8761e_gpio_chip.ngpio = 14;
+
+       err = gpiochip_add(&it8761e_gpio_chip);
+       if (err < 0)
+               goto gpiochip_add_err;
+
+       return 0;
+
+gpiochip_add_err:
+       release_region(gpio_ba, GPIO_IOSIZE);
+       gpio_ba = 0;
+       return err;
+}
+
+static void __exit it8761e_gpio_exit(void)
+{
+       if (gpio_ba) {
+               gpiochip_remove(&it8761e_gpio_chip);
+
+               release_region(gpio_ba, GPIO_IOSIZE);
+               gpio_ba = 0;
+       }
+}
+module_init(it8761e_gpio_init);
+module_exit(it8761e_gpio_exit);
+
+MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
+MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/max7300.c b/drivers/gpio/max7300.c
new file mode 100644 (file)
index 0000000..9d74eef
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * drivers/gpio/max7300.c
+ *
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix
+ *
+ * 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.
+ *
+ * Check max730x.c for further details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/spi/max7301.h>
+
+static int max7300_i2c_write(struct device *dev, unsigned int reg,
+                               unsigned int val)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int max7300_i2c_read(struct device *dev, unsigned int reg)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int __devinit max7300_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct max7301 *ts;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter,
+                       I2C_FUNC_SMBUS_BYTE_DATA))
+               return -EIO;
+
+       ts = kzalloc(sizeof(struct max7301), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       ts->read = max7300_i2c_read;
+       ts->write = max7300_i2c_write;
+       ts->dev = &client->dev;
+
+       ret = __max730x_probe(ts);
+       if (ret)
+               kfree(ts);
+       return ret;
+}
+
+static int __devexit max7300_remove(struct i2c_client *client)
+{
+       return __max730x_remove(&client->dev);
+}
+
+static const struct i2c_device_id max7300_id[] = {
+       { "max7300", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max7300_id);
+
+static struct i2c_driver max7300_driver = {
+       .driver = {
+               .name = "max7300",
+               .owner = THIS_MODULE,
+       },
+       .probe = max7300_probe,
+       .remove = __devexit_p(max7300_remove),
+       .id_table = max7300_id,
+};
+
+static int __init max7300_init(void)
+{
+       return i2c_add_driver(&max7300_driver);
+}
+subsys_initcall(max7300_init);
+
+static void __exit max7300_exit(void)
+{
+       i2c_del_driver(&max7300_driver);
+}
+module_exit(max7300_exit);
+
+MODULE_AUTHOR("Wolfram Sang");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MAX7300 GPIO-Expander");
index 480956f1ca50560c58f2c389b99bdbdf6036f38e..965d9b1ea13edf88f53ba780aa3fa2c57be48d36 100644 (file)
@@ -1,98 +1,41 @@
-/**
+/*
  * drivers/gpio/max7301.c
  *
  * Copyright (C) 2006 Juergen Beisert, Pengutronix
  * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * The Maxim's MAX7301 device is an SPI driven GPIO expander. There are
- * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more
- * details
- * Note:
- * - DIN must be stable at the rising edge of clock.
- * - when writing:
- *   - always clock in 16 clocks at once
- *   - at DIN: D15 first, D0 last
- *   - D0..D7 = databyte, D8..D14 = commandbyte
- *   - D15 = low -> write command
- * - when reading
- *   - always clock in 16 clocks at once
- *   - at DIN: D15 first, D0 last
- *   - D0..D7 = dummy, D8..D14 = register address
- *   - D15 = high -> read command
- *   - raise CS and assert it again
- *   - always clock in 16 clocks at once
- *   - at DOUT: D15 first, D0 last
- *   - D0..D7 contains the data from the first cycle
- *
- * The driver exports a standard gpiochip interface
+ * Check max730x.c for further details.
  */
 
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/max7301.h>
-#include <linux/gpio.h>
-
-#define DRIVER_NAME "max7301"
-
-/*
- * Pin configurations, see MAX7301 datasheet page 6
- */
-#define PIN_CONFIG_MASK 0x03
-#define PIN_CONFIG_IN_PULLUP 0x03
-#define PIN_CONFIG_IN_WO_PULLUP 0x02
-#define PIN_CONFIG_OUT 0x01
-
-#define PIN_NUMBER 28
-
-
-/*
- * Some registers must be read back to modify.
- * To save time we cache them here in memory
- */
-struct max7301 {
-       struct mutex    lock;
-       u8              port_config[8]; /* field 0 is unused */
-       u32             out_level;      /* cached output levels */
-       struct gpio_chip chip;
-       struct spi_device *spi;
-};
 
-/**
- * max7301_write - Write a new register content
- * @spi: The SPI device
- * @reg: Register offset
- * @val: Value to write
- *
- * A write to the MAX7301 means one message with one transfer
- *
- * Returns 0 if successful or a negative value on error
- */
-static int max7301_write(struct spi_device *spi, unsigned int reg, unsigned int val)
+/* A write to the MAX7301 means one message with one transfer */
+static int max7301_spi_write(struct device *dev, unsigned int reg,
+                               unsigned int val)
 {
+       struct spi_device *spi = to_spi_device(dev);
        u16 word = ((reg & 0x7F) << 8) | (val & 0xFF);
+
        return spi_write(spi, (const u8 *)&word, sizeof(word));
 }
 
-/**
- * max7301_read - Read back register content
- * @spi: The SPI device
- * @reg: Register offset
- *
- * A read from the MAX7301 means two transfers; here, one message each
- *
- * Returns positive 8 bit value from device if successful or a
- * negative value on error
- */
-static int max7301_read(struct spi_device *spi, unsigned int reg)
+/* A read from the MAX7301 means two transfers; here, one message each */
+
+static int max7301_spi_read(struct device *dev, unsigned int reg)
 {
        int ret;
        u16 word;
+       struct spi_device *spi = to_spi_device(dev);
 
        word = 0x8000 | (reg << 8);
        ret = spi_write(spi, (const u8 *)&word, sizeof(word));
@@ -108,125 +51,13 @@ static int max7301_read(struct spi_device *spi, unsigned int reg)
        return word & 0xff;
 }
 
-static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
-{
-       struct max7301 *ts = container_of(chip, struct max7301, chip);
-       u8 *config;
-       int ret;
-
-       /* First 4 pins are unused in the controller */
-       offset += 4;
-
-       config = &ts->port_config[offset >> 2];
-
-       mutex_lock(&ts->lock);
-
-       /* Standard GPIO API doesn't support pull-ups, has to be extended.
-        * Hard-coding no pollup for now. */
-       *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3));
-
-       ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config);
-
-       mutex_unlock(&ts->lock);
-
-       return ret;
-}
-
-static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
-{
-       if (value) {
-               ts->out_level |= 1 << offset;
-               return max7301_write(ts->spi, 0x20 + offset, 0x01);
-       } else {
-               ts->out_level &= ~(1 << offset);
-               return max7301_write(ts->spi, 0x20 + offset, 0x00);
-       }
-}
-
-static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
-                                   int value)
-{
-       struct max7301 *ts = container_of(chip, struct max7301, chip);
-       u8 *config;
-       int ret;
-
-       /* First 4 pins are unused in the controller */
-       offset += 4;
-
-       config = &ts->port_config[offset >> 2];
-
-       mutex_lock(&ts->lock);
-
-       *config = (*config & ~(3 << (offset & 3))) | (1 << (offset & 3));
-
-       ret = __max7301_set(ts, offset, value);
-
-       if (!ret)
-               ret = max7301_write(ts->spi, 0x08 + (offset >> 2), *config);
-
-       mutex_unlock(&ts->lock);
-
-       return ret;
-}
-
-static int max7301_get(struct gpio_chip *chip, unsigned offset)
-{
-       struct max7301 *ts = container_of(chip, struct max7301, chip);
-       int config, level = -EINVAL;
-
-       /* First 4 pins are unused in the controller */
-       offset += 4;
-
-       mutex_lock(&ts->lock);
-
-       config = (ts->port_config[offset >> 2] >> ((offset & 3) * 2)) & 3;
-
-       switch (config) {
-       case 1:
-               /* Output: return cached level */
-               level =  !!(ts->out_level & (1 << offset));
-               break;
-       case 2:
-       case 3:
-               /* Input: read out */
-               level = max7301_read(ts->spi, 0x20 + offset) & 0x01;
-       }
-       mutex_unlock(&ts->lock);
-
-       return level;
-}
-
-static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
-{
-       struct max7301 *ts = container_of(chip, struct max7301, chip);
-
-       /* First 4 pins are unused in the controller */
-       offset += 4;
-
-       mutex_lock(&ts->lock);
-
-       __max7301_set(ts, offset, value);
-
-       mutex_unlock(&ts->lock);
-}
-
 static int __devinit max7301_probe(struct spi_device *spi)
 {
        struct max7301 *ts;
-       struct max7301_platform_data *pdata;
-       int i, ret;
-
-       pdata = spi->dev.platform_data;
-       if (!pdata || !pdata->base) {
-               dev_dbg(&spi->dev, "incorrect or missing platform data\n");
-               return -EINVAL;
-       }
+       int ret;
 
-       /*
-        * bits_per_word cannot be configured in platform data
-        */
+       /* bits_per_word cannot be configured in platform data */
        spi->bits_per_word = 16;
-
        ret = spi_setup(spi);
        if (ret < 0)
                return ret;
@@ -235,90 +66,35 @@ static int __devinit max7301_probe(struct spi_device *spi)
        if (!ts)
                return -ENOMEM;
 
-       mutex_init(&ts->lock);
-
-       dev_set_drvdata(&spi->dev, ts);
+       ts->read = max7301_spi_read;
+       ts->write = max7301_spi_write;
+       ts->dev = &spi->dev;
 
-       /* Power up the chip and disable IRQ output */
-       max7301_write(spi, 0x04, 0x01);
-
-       ts->spi = spi;
-
-       ts->chip.label = DRIVER_NAME,
-
-       ts->chip.direction_input = max7301_direction_input;
-       ts->chip.get = max7301_get;
-       ts->chip.direction_output = max7301_direction_output;
-       ts->chip.set = max7301_set;
-
-       ts->chip.base = pdata->base;
-       ts->chip.ngpio = PIN_NUMBER;
-       ts->chip.can_sleep = 1;
-       ts->chip.dev = &spi->dev;
-       ts->chip.owner = THIS_MODULE;
-
-       /*
-        * tristate all pins in hardware and cache the
-        * register values for later use.
-        */
-       for (i = 1; i < 8; i++) {
-               int j;
-               /* 0xAA means input with internal pullup disabled */
-               max7301_write(spi, 0x08 + i, 0xAA);
-               ts->port_config[i] = 0xAA;
-               for (j = 0; j < 4; j++) {
-                       int offset = (i - 1) * 4 + j;
-                       ret = max7301_direction_input(&ts->chip, offset);
-                       if (ret)
-                               goto exit_destroy;
-               }
-       }
-
-       ret = gpiochip_add(&ts->chip);
+       ret = __max730x_probe(ts);
        if (ret)
-               goto exit_destroy;
-
-       return ret;
-
-exit_destroy:
-       dev_set_drvdata(&spi->dev, NULL);
-       mutex_destroy(&ts->lock);
-       kfree(ts);
+               kfree(ts);
        return ret;
 }
 
 static int __devexit max7301_remove(struct spi_device *spi)
 {
-       struct max7301 *ts;
-       int ret;
-
-       ts = dev_get_drvdata(&spi->dev);
-       if (ts == NULL)
-               return -ENODEV;
-
-       dev_set_drvdata(&spi->dev, NULL);
-
-       /* Power down the chip and disable IRQ output */
-       max7301_write(spi, 0x04, 0x00);
-
-       ret = gpiochip_remove(&ts->chip);
-       if (!ret) {
-               mutex_destroy(&ts->lock);
-               kfree(ts);
-       } else
-               dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
-                       ret);
-
-       return ret;
+       return __max730x_remove(&spi->dev);
 }
 
+static const struct spi_device_id max7301_id[] = {
+       { "max7301", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, max7301_id);
+
 static struct spi_driver max7301_driver = {
        .driver = {
-               .name           = DRIVER_NAME,
-               .owner          = THIS_MODULE,
+               .name = "max7301",
+               .owner = THIS_MODULE,
        },
-       .probe          = max7301_probe,
-       .remove         = __devexit_p(max7301_remove),
+       .probe = max7301_probe,
+       .remove = __devexit_p(max7301_remove),
+       .id_table = max7301_id,
 };
 
 static int __init max7301_init(void)
@@ -336,7 +112,6 @@ static void __exit max7301_exit(void)
 }
 module_exit(max7301_exit);
 
-MODULE_AUTHOR("Juergen Beisert");
+MODULE_AUTHOR("Juergen Beisert, Wolfram Sang");
 MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("MAX7301 SPI based GPIO-Expander");
-MODULE_ALIAS("spi:" DRIVER_NAME);
+MODULE_DESCRIPTION("MAX7301 GPIO-Expander");
diff --git a/drivers/gpio/max730x.c b/drivers/gpio/max730x.c
new file mode 100644 (file)
index 0000000..c9bced5
--- /dev/null
@@ -0,0 +1,244 @@
+/**
+ * drivers/gpio/max7301.c
+ *
+ * Copyright (C) 2006 Juergen Beisert, Pengutronix
+ * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are
+ * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more
+ * details
+ * Note:
+ * - DIN must be stable at the rising edge of clock.
+ * - when writing:
+ *   - always clock in 16 clocks at once
+ *   - at DIN: D15 first, D0 last
+ *   - D0..D7 = databyte, D8..D14 = commandbyte
+ *   - D15 = low -> write command
+ * - when reading
+ *   - always clock in 16 clocks at once
+ *   - at DIN: D15 first, D0 last
+ *   - D0..D7 = dummy, D8..D14 = register address
+ *   - D15 = high -> read command
+ *   - raise CS and assert it again
+ *   - always clock in 16 clocks at once
+ *   - at DOUT: D15 first, D0 last
+ *   - D0..D7 contains the data from the first cycle
+ *
+ * The driver exports a standard gpiochip interface
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/spi/max7301.h>
+#include <linux/gpio.h>
+
+/*
+ * Pin configurations, see MAX7301 datasheet page 6
+ */
+#define PIN_CONFIG_MASK 0x03
+#define PIN_CONFIG_IN_PULLUP 0x03
+#define PIN_CONFIG_IN_WO_PULLUP 0x02
+#define PIN_CONFIG_OUT 0x01
+
+#define PIN_NUMBER 28
+
+static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct max7301 *ts = container_of(chip, struct max7301, chip);
+       u8 *config;
+       u8 offset_bits;
+       int ret;
+
+       /* First 4 pins are unused in the controller */
+       offset += 4;
+       offset_bits = (offset & 3) << 1;
+
+       config = &ts->port_config[offset >> 2];
+
+       mutex_lock(&ts->lock);
+
+       /* Standard GPIO API doesn't support pull-ups, has to be extended.
+        * Hard-coding no pollup for now. */
+       *config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
+                          | (PIN_CONFIG_IN_WO_PULLUP << offset_bits);
+
+       ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
+
+       mutex_unlock(&ts->lock);
+
+       return ret;
+}
+
+static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
+{
+       if (value) {
+               ts->out_level |= 1 << offset;
+               return ts->write(ts->dev, 0x20 + offset, 0x01);
+       } else {
+               ts->out_level &= ~(1 << offset);
+               return ts->write(ts->dev, 0x20 + offset, 0x00);
+       }
+}
+
+static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
+                                   int value)
+{
+       struct max7301 *ts = container_of(chip, struct max7301, chip);
+       u8 *config;
+       u8 offset_bits;
+       int ret;
+
+       /* First 4 pins are unused in the controller */
+       offset += 4;
+       offset_bits = (offset & 3) << 1;
+
+       config = &ts->port_config[offset >> 2];
+
+       mutex_lock(&ts->lock);
+
+       *config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
+                          | (PIN_CONFIG_OUT << offset_bits);
+
+       ret = __max7301_set(ts, offset, value);
+
+       if (!ret)
+               ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
+
+       mutex_unlock(&ts->lock);
+
+       return ret;
+}
+
+static int max7301_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct max7301 *ts = container_of(chip, struct max7301, chip);
+       int config, level = -EINVAL;
+
+       /* First 4 pins are unused in the controller */
+       offset += 4;
+
+       mutex_lock(&ts->lock);
+
+       config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1))
+                       & PIN_CONFIG_MASK;
+
+       switch (config) {
+       case PIN_CONFIG_OUT:
+               /* Output: return cached level */
+               level =  !!(ts->out_level & (1 << offset));
+               break;
+       case PIN_CONFIG_IN_WO_PULLUP:
+       case PIN_CONFIG_IN_PULLUP:
+               /* Input: read out */
+               level = ts->read(ts->dev, 0x20 + offset) & 0x01;
+       }
+       mutex_unlock(&ts->lock);
+
+       return level;
+}
+
+static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct max7301 *ts = container_of(chip, struct max7301, chip);
+
+       /* First 4 pins are unused in the controller */
+       offset += 4;
+
+       mutex_lock(&ts->lock);
+
+       __max7301_set(ts, offset, value);
+
+       mutex_unlock(&ts->lock);
+}
+
+int __devinit __max730x_probe(struct max7301 *ts)
+{
+       struct device *dev = ts->dev;
+       struct max7301_platform_data *pdata;
+       int i, ret;
+
+       pdata = dev->platform_data;
+       if (!pdata || !pdata->base) {
+               dev_err(dev, "incorrect or missing platform data\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&ts->lock);
+       dev_set_drvdata(dev, ts);
+
+       /* Power up the chip and disable IRQ output */
+       ts->write(dev, 0x04, 0x01);
+
+       ts->chip.label = dev->driver->name;
+
+       ts->chip.direction_input = max7301_direction_input;
+       ts->chip.get = max7301_get;
+       ts->chip.direction_output = max7301_direction_output;
+       ts->chip.set = max7301_set;
+
+       ts->chip.base = pdata->base;
+       ts->chip.ngpio = PIN_NUMBER;
+       ts->chip.can_sleep = 1;
+       ts->chip.dev = dev;
+       ts->chip.owner = THIS_MODULE;
+
+       /*
+        * tristate all pins in hardware and cache the
+        * register values for later use.
+        */
+       for (i = 1; i < 8; i++) {
+               int j;
+               /* 0xAA means input with internal pullup disabled */
+               ts->write(dev, 0x08 + i, 0xAA);
+               ts->port_config[i] = 0xAA;
+               for (j = 0; j < 4; j++) {
+                       int offset = (i - 1) * 4 + j;
+                       ret = max7301_direction_input(&ts->chip, offset);
+                       if (ret)
+                               goto exit_destroy;
+               }
+       }
+
+       ret = gpiochip_add(&ts->chip);
+       if (ret)
+               goto exit_destroy;
+
+       return ret;
+
+exit_destroy:
+       dev_set_drvdata(dev, NULL);
+       mutex_destroy(&ts->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__max730x_probe);
+
+int __devexit __max730x_remove(struct device *dev)
+{
+       struct max7301 *ts = dev_get_drvdata(dev);
+       int ret;
+
+       if (ts == NULL)
+               return -ENODEV;
+
+       dev_set_drvdata(dev, NULL);
+
+       /* Power down the chip and disable IRQ output */
+       ts->write(dev, 0x04, 0x00);
+
+       ret = gpiochip_remove(&ts->chip);
+       if (!ret) {
+               mutex_destroy(&ts->lock);
+               kfree(ts);
+       } else
+               dev_err(dev, "Failed to remove GPIO controller: %d\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__max730x_remove);
index 6a2fb3fbb3d965bae1956b0bf9b3bc6f307b22c7..ab5daab14bc2589e884316018ddaf0a2fc9a0d62 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/i2c.h>
 #include <linux/i2c/pca953x.h>
 #ifdef CONFIG_OF_GPIO
 #define PCA953X_INVERT         2
 #define PCA953X_DIRECTION      3
 
+#define PCA953X_GPIOS         0x00FF
+#define PCA953X_INT           0x0100
+
 static const struct i2c_device_id pca953x_id[] = {
-       { "pca9534", 8, },
-       { "pca9535", 16, },
+       { "pca9534", 8  | PCA953X_INT, },
+       { "pca9535", 16 | PCA953X_INT, },
        { "pca9536", 4, },
-       { "pca9537", 4, },
-       { "pca9538", 8, },
-       { "pca9539", 16, },
-       { "pca9554", 8, },
-       { "pca9555", 16, },
+       { "pca9537", 4  | PCA953X_INT, },
+       { "pca9538", 8  | PCA953X_INT, },
+       { "pca9539", 16 | PCA953X_INT, },
+       { "pca9554", 8  | PCA953X_INT, },
+       { "pca9555", 16 | PCA953X_INT, },
        { "pca9556", 8, },
        { "pca9557", 8, },
 
        { "max7310", 8, },
-       { "max7315", 8, },
-       { "pca6107", 8, },
-       { "tca6408", 8, },
-       { "tca6416", 16, },
+       { "max7312", 16 | PCA953X_INT, },
+       { "max7313", 16 | PCA953X_INT, },
+       { "max7315", 8  | PCA953X_INT, },
+       { "pca6107", 8  | PCA953X_INT, },
+       { "tca6408", 8  | PCA953X_INT, },
+       { "tca6416", 16 | PCA953X_INT, },
        /* NYET:  { "tca6424", 24, }, */
        { }
 };
@@ -53,6 +60,15 @@ struct pca953x_chip {
        uint16_t reg_output;
        uint16_t reg_direction;
 
+#ifdef CONFIG_GPIO_PCA953X_IRQ
+       struct mutex irq_lock;
+       uint16_t irq_mask;
+       uint16_t irq_stat;
+       uint16_t irq_trig_raise;
+       uint16_t irq_trig_fall;
+       int      irq_base;
+#endif
+
        struct i2c_client *client;
        struct pca953x_platform_data *dyn_pdata;
        struct gpio_chip gpio_chip;
@@ -202,6 +218,210 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
        gc->names = chip->names;
 }
 
+#ifdef CONFIG_GPIO_PCA953X_IRQ
+static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
+{
+       struct pca953x_chip *chip;
+
+       chip = container_of(gc, struct pca953x_chip, gpio_chip);
+       return chip->irq_base + off;
+}
+
+static void pca953x_irq_mask(unsigned int irq)
+{
+       struct pca953x_chip *chip = get_irq_chip_data(irq);
+
+       chip->irq_mask &= ~(1 << (irq - chip->irq_base));
+}
+
+static void pca953x_irq_unmask(unsigned int irq)
+{
+       struct pca953x_chip *chip = get_irq_chip_data(irq);
+
+       chip->irq_mask |= 1 << (irq - chip->irq_base);
+}
+
+static void pca953x_irq_bus_lock(unsigned int irq)
+{
+       struct pca953x_chip *chip = get_irq_chip_data(irq);
+
+       mutex_lock(&chip->irq_lock);
+}
+
+static void pca953x_irq_bus_sync_unlock(unsigned int irq)
+{
+       struct pca953x_chip *chip = get_irq_chip_data(irq);
+
+       mutex_unlock(&chip->irq_lock);
+}
+
+static int pca953x_irq_set_type(unsigned int irq, unsigned int type)
+{
+       struct pca953x_chip *chip = get_irq_chip_data(irq);
+       uint16_t level = irq - chip->irq_base;
+       uint16_t mask = 1 << level;
+
+       if (!(type & IRQ_TYPE_EDGE_BOTH)) {
+               dev_err(&chip->client->dev, "irq %d: unsupported type %d\n",
+                       irq, type);
+               return -EINVAL;
+       }
+
+       if (type & IRQ_TYPE_EDGE_FALLING)
+               chip->irq_trig_fall |= mask;
+       else
+               chip->irq_trig_fall &= ~mask;
+
+       if (type & IRQ_TYPE_EDGE_RISING)
+               chip->irq_trig_raise |= mask;
+       else
+               chip->irq_trig_raise &= ~mask;
+
+       return pca953x_gpio_direction_input(&chip->gpio_chip, level);
+}
+
+static struct irq_chip pca953x_irq_chip = {
+       .name                   = "pca953x",
+       .mask                   = pca953x_irq_mask,
+       .unmask                 = pca953x_irq_unmask,
+       .bus_lock               = pca953x_irq_bus_lock,
+       .bus_sync_unlock        = pca953x_irq_bus_sync_unlock,
+       .set_type               = pca953x_irq_set_type,
+};
+
+static uint16_t pca953x_irq_pending(struct pca953x_chip *chip)
+{
+       uint16_t cur_stat;
+       uint16_t old_stat;
+       uint16_t pending;
+       uint16_t trigger;
+       int ret;
+
+       ret = pca953x_read_reg(chip, PCA953X_INPUT, &cur_stat);
+       if (ret)
+               return 0;
+
+       /* Remove output pins from the equation */
+       cur_stat &= chip->reg_direction;
+
+       old_stat = chip->irq_stat;
+       trigger = (cur_stat ^ old_stat) & chip->irq_mask;
+
+       if (!trigger)
+               return 0;
+
+       chip->irq_stat = cur_stat;
+
+       pending = (old_stat & chip->irq_trig_fall) |
+                 (cur_stat & chip->irq_trig_raise);
+       pending &= trigger;
+
+       return pending;
+}
+
+static irqreturn_t pca953x_irq_handler(int irq, void *devid)
+{
+       struct pca953x_chip *chip = devid;
+       uint16_t pending;
+       uint16_t level;
+
+       pending = pca953x_irq_pending(chip);
+
+       if (!pending)
+               return IRQ_HANDLED;
+
+       do {
+               level = __ffs(pending);
+               handle_nested_irq(level + chip->irq_base);
+
+               pending &= ~(1 << level);
+       } while (pending);
+
+       return IRQ_HANDLED;
+}
+
+static int pca953x_irq_setup(struct pca953x_chip *chip,
+                            const struct i2c_device_id *id)
+{
+       struct i2c_client *client = chip->client;
+       struct pca953x_platform_data *pdata = client->dev.platform_data;
+       int ret;
+
+       if (pdata->irq_base && (id->driver_data & PCA953X_INT)) {
+               int lvl;
+
+               ret = pca953x_read_reg(chip, PCA953X_INPUT,
+                                      &chip->irq_stat);
+               if (ret)
+                       goto out_failed;
+
+               /*
+                * There is no way to know which GPIO line generated the
+                * interrupt.  We have to rely on the previous read for
+                * this purpose.
+                */
+               chip->irq_stat &= chip->reg_direction;
+               chip->irq_base = pdata->irq_base;
+               mutex_init(&chip->irq_lock);
+
+               for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
+                       int irq = lvl + chip->irq_base;
+
+                       set_irq_chip_data(irq, chip);
+                       set_irq_chip_and_handler(irq, &pca953x_irq_chip,
+                                                handle_edge_irq);
+                       set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+                       set_irq_flags(irq, IRQF_VALID);
+#else
+                       set_irq_noprobe(irq);
+#endif
+               }
+
+               ret = request_threaded_irq(client->irq,
+                                          NULL,
+                                          pca953x_irq_handler,
+                                          IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                          dev_name(&client->dev), chip);
+               if (ret) {
+                       dev_err(&client->dev, "failed to request irq %d\n",
+                               client->irq);
+                       goto out_failed;
+               }
+
+               chip->gpio_chip.to_irq = pca953x_gpio_to_irq;
+       }
+
+       return 0;
+
+out_failed:
+       chip->irq_base = 0;
+       return ret;
+}
+
+static void pca953x_irq_teardown(struct pca953x_chip *chip)
+{
+       if (chip->irq_base)
+               free_irq(chip->client->irq, chip);
+}
+#else /* CONFIG_GPIO_PCA953X_IRQ */
+static int pca953x_irq_setup(struct pca953x_chip *chip,
+                            const struct i2c_device_id *id)
+{
+       struct i2c_client *client = chip->client;
+       struct pca953x_platform_data *pdata = client->dev.platform_data;
+
+       if (pdata->irq_base && (id->driver_data & PCA953X_INT))
+               dev_warn(&client->dev, "interrupt support not compiled in\n");
+
+       return 0;
+}
+
+static void pca953x_irq_teardown(struct pca953x_chip *chip)
+{
+}
+#endif
+
 /*
  * Handlers for alternative sources of platform_data
  */
@@ -286,7 +506,7 @@ static int __devinit pca953x_probe(struct i2c_client *client,
        /* initialize cached registers from their original values.
         * we can't share this chip with another i2c master.
         */
-       pca953x_setup_gpio(chip, id->driver_data);
+       pca953x_setup_gpio(chip, id->driver_data & PCA953X_GPIOS);
 
        ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output);
        if (ret)
@@ -301,6 +521,9 @@ static int __devinit pca953x_probe(struct i2c_client *client,
        if (ret)
                goto out_failed;
 
+       ret = pca953x_irq_setup(chip, id);
+       if (ret)
+               goto out_failed;
 
        ret = gpiochip_add(&chip->gpio_chip);
        if (ret)
@@ -317,6 +540,7 @@ static int __devinit pca953x_probe(struct i2c_client *client,
        return 0;
 
 out_failed:
+       pca953x_irq_teardown(chip);
        kfree(chip->dyn_pdata);
        kfree(chip);
        return ret;
@@ -345,6 +569,7 @@ static int pca953x_remove(struct i2c_client *client)
                return ret;
        }
 
+       pca953x_irq_teardown(chip);
        kfree(chip->dyn_pdata);
        kfree(chip);
        return 0;
index 4ee4c8367a3f988144c7a721399a5d14306be50e..3ad1eeb496099cceb8118d5666460377aec8d5d0 100644 (file)
@@ -219,7 +219,7 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc)
                if (pending == 0)
                        continue;
 
-               for_each_bit(offset, &pending, PL061_GPIO_NR)
+               for_each_set_bit(offset, &pending, PL061_GPIO_NR)
                        generic_handle_irq(pl061_to_irq(&chip->gc, offset));
        }
        desc->chip->unmask(irq);
index a4d344ba8e5cd10868995f9edd6c3c98e4a09080..d4295fa5369e4bc129e498b69be67ba52ac645ee 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/module.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
+#include <linux/irq.h>
 #include <linux/io.h>
 #include <linux/timb_gpio.h>
 #include <linux/interrupt.h>
@@ -37,6 +38,8 @@
 #define TGPIO_ICR      0x14
 #define TGPIO_FLR      0x18
 #define TGPIO_LVR      0x1c
+#define TGPIO_VER      0x20
+#define TGPIO_BFLR     0x24
 
 struct timbgpio {
        void __iomem            *membase;
@@ -125,17 +128,23 @@ static int timbgpio_irq_type(unsigned irq, unsigned trigger)
        struct timbgpio *tgpio = get_irq_chip_data(irq);
        int offset = irq - tgpio->irq_base;
        unsigned long flags;
-       u32 lvr, flr;
+       u32 lvr, flr, bflr = 0;
+       u32 ver;
 
        if (offset < 0 || offset > tgpio->gpio.ngpio)
                return -EINVAL;
 
+       ver = ioread32(tgpio->membase + TGPIO_VER);
+
        spin_lock_irqsave(&tgpio->lock, flags);
 
        lvr = ioread32(tgpio->membase + TGPIO_LVR);
        flr = ioread32(tgpio->membase + TGPIO_FLR);
+       if (ver > 2)
+               bflr = ioread32(tgpio->membase + TGPIO_BFLR);
 
        if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+               bflr &= ~(1 << offset);
                flr &= ~(1 << offset);
                if (trigger & IRQ_TYPE_LEVEL_HIGH)
                        lvr |= 1 << offset;
@@ -143,21 +152,27 @@ static int timbgpio_irq_type(unsigned irq, unsigned trigger)
                        lvr &= ~(1 << offset);
        }
 
-       if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-               return -EINVAL;
-       else {
+       if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+               if (ver < 3)
+                       return -EINVAL;
+               else {
+                       flr |= 1 << offset;
+                       bflr |= 1 << offset;
+               }
+       } else {
+               bflr &= ~(1 << offset);
                flr |= 1 << offset;
-               /* opposite compared to the datasheet, but it mirrors the
-                * reality
-                */
                if (trigger & IRQ_TYPE_EDGE_FALLING)
-                       lvr |= 1 << offset;
-               else
                        lvr &= ~(1 << offset);
+               else
+                       lvr |= 1 << offset;
        }
 
        iowrite32(lvr, tgpio->membase + TGPIO_LVR);
        iowrite32(flr, tgpio->membase + TGPIO_FLR);
+       if (ver > 2)
+               iowrite32(bflr, tgpio->membase + TGPIO_BFLR);
+
        iowrite32(1 << offset, tgpio->membase + TGPIO_ICR);
        spin_unlock_irqrestore(&tgpio->lock, flags);
 
@@ -174,7 +189,7 @@ static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
        ipr = ioread32(tgpio->membase + TGPIO_IPR);
        iowrite32(ipr, tgpio->membase + TGPIO_ICR);
 
-       for_each_bit(offset, &ipr, tgpio->gpio.ngpio)
+       for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio)
                generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
 }
 
index 39c5aa75b8f119eb6cc3263e44bc592ef25b9f30..abe3f446ca48efe06d7896d00b4c347a2e6daead 100644 (file)
@@ -4,7 +4,7 @@
 
 ccflags-y := -Iinclude/drm
 
-drm-y       := drm_auth.o drm_bufs.o drm_cache.o \
+drm-y       := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
                drm_context.o drm_dma.o drm_drawable.o \
                drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
                drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
diff --git a/drivers/gpu/drm/drm_buffer.c b/drivers/gpu/drm/drm_buffer.c
new file mode 100644 (file)
index 0000000..55d03ed
--- /dev/null
@@ -0,0 +1,184 @@
+/**************************************************************************
+ *
+ * Copyright 2010 Pauli Nieminen.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+/*
+ * Multipart buffer for coping data which is larger than the page size.
+ *
+ * Authors:
+ * Pauli Nieminen <suokkos-at-gmail-dot-com>
+ */
+
+#include "drm_buffer.h"
+
+/**
+ * Allocate the drm buffer object.
+ *
+ *   buf: Pointer to a pointer where the object is stored.
+ *   size: The number of bytes to allocate.
+ */
+int drm_buffer_alloc(struct drm_buffer **buf, int size)
+{
+       int nr_pages = size / PAGE_SIZE + 1;
+       int idx;
+
+       /* Allocating pointer table to end of structure makes drm_buffer
+        * variable sized */
+       *buf = kzalloc(sizeof(struct drm_buffer) + nr_pages*sizeof(char *),
+                       GFP_KERNEL);
+
+       if (*buf == NULL) {
+               DRM_ERROR("Failed to allocate drm buffer object to hold"
+                               " %d bytes in %d pages.\n",
+                               size, nr_pages);
+               return -ENOMEM;
+       }
+
+       (*buf)->size = size;
+
+       for (idx = 0; idx < nr_pages; ++idx) {
+
+               (*buf)->data[idx] =
+                       kmalloc(min(PAGE_SIZE, size - idx * PAGE_SIZE),
+                               GFP_KERNEL);
+
+
+               if ((*buf)->data[idx] == NULL) {
+                       DRM_ERROR("Failed to allocate %dth page for drm"
+                                       " buffer with %d bytes and %d pages.\n",
+                                       idx + 1, size, nr_pages);
+                       goto error_out;
+               }
+
+       }
+
+       return 0;
+
+error_out:
+
+       /* Only last element can be null pointer so check for it first. */
+       if ((*buf)->data[idx])
+               kfree((*buf)->data[idx]);
+
+       for (--idx; idx >= 0; --idx)
+               kfree((*buf)->data[idx]);
+
+       kfree(*buf);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_buffer_alloc);
+
+/**
+ * Copy the user data to the begin of the buffer and reset the processing
+ * iterator.
+ *
+ *   user_data: A pointer the data that is copied to the buffer.
+ *   size: The Number of bytes to copy.
+ */
+extern int drm_buffer_copy_from_user(struct drm_buffer *buf,
+               void __user *user_data, int size)
+{
+       int nr_pages = size / PAGE_SIZE + 1;
+       int idx;
+
+       if (size > buf->size) {
+               DRM_ERROR("Requesting to copy %d bytes to a drm buffer with"
+                               " %d bytes space\n",
+                               size, buf->size);
+               return -EFAULT;
+       }
+
+       for (idx = 0; idx < nr_pages; ++idx) {
+
+               if (DRM_COPY_FROM_USER(buf->data[idx],
+                       user_data + idx * PAGE_SIZE,
+                       min(PAGE_SIZE, size - idx * PAGE_SIZE))) {
+                       DRM_ERROR("Failed to copy user data (%p) to drm buffer"
+                                       " (%p) %dth page.\n",
+                                       user_data, buf, idx);
+                       return -EFAULT;
+
+               }
+       }
+       buf->iterator = 0;
+       return 0;
+}
+EXPORT_SYMBOL(drm_buffer_copy_from_user);
+
+/**
+ * Free the drm buffer object
+ */
+void drm_buffer_free(struct drm_buffer *buf)
+{
+
+       if (buf != NULL) {
+
+               int nr_pages = buf->size / PAGE_SIZE + 1;
+               int idx;
+               for (idx = 0; idx < nr_pages; ++idx)
+                       kfree(buf->data[idx]);
+
+               kfree(buf);
+       }
+}
+EXPORT_SYMBOL(drm_buffer_free);
+
+/**
+ * Read an object from buffer that may be split to multiple parts. If object
+ * is not split function just returns the pointer to object in buffer. But in
+ * case of split object data is copied to given stack object that is suplied
+ * by caller.
+ *
+ * The processing location of the buffer is also advanced to the next byte
+ * after the object.
+ *
+ *   objsize: The size of the objet in bytes.
+ *   stack_obj: A pointer to a memory location where object can be copied.
+ */
+void *drm_buffer_read_object(struct drm_buffer *buf,
+               int objsize, void *stack_obj)
+{
+       int idx = drm_buffer_index(buf);
+       int page = drm_buffer_page(buf);
+       void *obj = 0;
+
+       if (idx + objsize <= PAGE_SIZE) {
+               obj = &buf->data[page][idx];
+       } else {
+               /* The object is split which forces copy to temporary object.*/
+               int beginsz = PAGE_SIZE - idx;
+               memcpy(stack_obj, &buf->data[page][idx], beginsz);
+
+               memcpy(stack_obj + beginsz, &buf->data[page + 1][0],
+                               objsize - beginsz);
+
+               obj = stack_obj;
+       }
+
+       drm_buffer_advance(buf, objsize);
+       return obj;
+}
+EXPORT_SYMBOL(drm_buffer_read_object);
index 7d0f00a935faa46c2bc60d15027d2edf1ba29397..f2aaf39be3981944fefc63a23fd8edcc9e3a614f 100644 (file)
@@ -836,11 +836,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
                        mode_changed = true;
                } else if (set->fb == NULL) {
                        mode_changed = true;
-               } else if ((set->fb->bits_per_pixel !=
-                        set->crtc->fb->bits_per_pixel) ||
-                        set->fb->depth != set->crtc->fb->depth)
-                       fb_changed = true;
-               else
+               } else
                        fb_changed = true;
        }
 
index 766c46875a2047bae1001a52c6ff9cb9a90d8341..f3c58e2bd75cde20720712d3416229bcece0bb27 100644 (file)
@@ -125,28 +125,28 @@ static struct drm_ioctl_desc drm_ioctls[] = {
 
        DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 
-       DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0),
-       DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH),
-
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
-       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW)
+       DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
+
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
index ab6c973304129383ca733eeb2f49b117b55b421a..f97e7c42ac8e1f7bdf53573e491feb0a80f2989f 100644 (file)
@@ -60,8 +60,7 @@
 #define EDID_QUIRK_FIRST_DETAILED_PREFERRED    (1 << 5)
 /* use +hsync +vsync for detailed mode */
 #define EDID_QUIRK_DETAILED_SYNC_PP            (1 << 6)
-/* define the number of Extension EDID block */
-#define MAX_EDID_EXT_NUM 4
+
 
 #define LEVEL_DMT      0
 #define LEVEL_GTF      1
@@ -114,14 +113,14 @@ static const u8 edid_header[] = {
 };
 
 /**
- * edid_is_valid - sanity check EDID data
+ * drm_edid_is_valid - sanity check EDID data
  * @edid: EDID data
  *
  * Sanity check the EDID block by looking at the header, the version number
  * and the checksum.  Return 0 if the EDID doesn't check out, or 1 if it's
  * valid.
  */
-static bool edid_is_valid(struct edid *edid)
+bool drm_edid_is_valid(struct edid *edid)
 {
        int i, score = 0;
        u8 csum = 0;
@@ -163,6 +162,7 @@ bad:
        }
        return 0;
 }
+EXPORT_SYMBOL(drm_edid_is_valid);
 
 /**
  * edid_vendor - match a string against EDID's obfuscated vendor field
@@ -1112,8 +1112,8 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
        }
 
        /* Chose real EDID extension number */
-       edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
-                      MAX_EDID_EXT_NUM : edid->extensions;
+       edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
+               DRM_MAX_EDID_EXT_NUM : edid->extensions;
 
        /* Find CEA extension */
        for (i = 0; i < edid_ext_num; i++) {
@@ -1195,7 +1195,7 @@ static int drm_ddc_read_edid(struct drm_connector *connector,
        for (i = 0; i < 4; i++) {
                if (drm_do_probe_ddc_edid(adapter, buf, len))
                        return -1;
-               if (edid_is_valid((struct edid *)buf))
+               if (drm_edid_is_valid((struct edid *)buf))
                        return 0;
        }
 
@@ -1220,7 +1220,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
        int ret;
        struct edid *edid;
 
-       edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1),
+       edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
                       GFP_KERNEL);
        if (edid == NULL) {
                dev_warn(&connector->dev->pdev->dev,
@@ -1238,14 +1238,14 @@ struct edid *drm_get_edid(struct drm_connector *connector,
        if (edid->extensions != 0) {
                int edid_ext_num = edid->extensions;
 
-               if (edid_ext_num > MAX_EDID_EXT_NUM) {
+               if (edid_ext_num > DRM_MAX_EDID_EXT_NUM) {
                        dev_warn(&connector->dev->pdev->dev,
                                 "The number of extension(%d) is "
                                 "over max (%d), actually read number (%d)\n",
-                                edid_ext_num, MAX_EDID_EXT_NUM,
-                                MAX_EDID_EXT_NUM);
+                                edid_ext_num, DRM_MAX_EDID_EXT_NUM,
+                                DRM_MAX_EDID_EXT_NUM);
                        /* Reset EDID extension number to be read */
-                       edid_ext_num = MAX_EDID_EXT_NUM;
+                       edid_ext_num = DRM_MAX_EDID_EXT_NUM;
                }
                /* Read EDID including extensions too */
                ret = drm_ddc_read_edid(connector, adapter, (char *)edid,
@@ -1288,8 +1288,8 @@ bool drm_detect_hdmi_monitor(struct edid *edid)
                goto end;
 
        /* Chose real EDID extension number */
-       edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
-                      MAX_EDID_EXT_NUM : edid->extensions;
+       edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
+                      DRM_MAX_EDID_EXT_NUM : edid->extensions;
 
        /* Find CEA extension */
        for (i = 0; i < edid_ext_num; i++) {
@@ -1346,7 +1346,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        if (edid == NULL) {
                return 0;
        }
-       if (!edid_is_valid(edid)) {
+       if (!drm_edid_is_valid(edid)) {
                dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
                         drm_get_connector_name(connector));
                return 0;
index 0f9e90552dc44098589e8bf20a1154266c25db67..50549703584f5b7d91c54d24f037dfa5d26f5ebc 100644 (file)
@@ -27,6 +27,7 @@
  *      Dave Airlie <airlied@linux.ie>
  *      Jesse Barnes <jesse.barnes@intel.com>
  */
+#include <linux/kernel.h>
 #include <linux/sysrq.h>
 #include <linux/fb.h>
 #include "drmP.h"
@@ -50,21 +51,6 @@ int drm_fb_helper_add_connector(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_fb_helper_add_connector);
 
-static int my_atoi(const char *name)
-{
-       int val = 0;
-
-       for (;; name++) {
-               switch (*name) {
-               case '0' ... '9':
-                       val = 10*val+(*name-'0');
-                       break;
-               default:
-                       return val;
-               }
-       }
-}
-
 /**
  * drm_fb_helper_connector_parse_command_line - parse command line for connector
  * @connector - connector to parse line for
@@ -111,7 +97,7 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
                        namelen = i;
                        if (!refresh_specified && !bpp_specified &&
                            !yres_specified) {
-                               refresh = my_atoi(&name[i+1]);
+                               refresh = simple_strtol(&name[i+1], NULL, 10);
                                refresh_specified = 1;
                                if (cvt || rb)
                                        cvt = 0;
@@ -121,7 +107,7 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
                case '-':
                        namelen = i;
                        if (!bpp_specified && !yres_specified) {
-                               bpp = my_atoi(&name[i+1]);
+                               bpp = simple_strtol(&name[i+1], NULL, 10);
                                bpp_specified = 1;
                                if (cvt || rb)
                                        cvt = 0;
@@ -130,7 +116,7 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
                        break;
                case 'x':
                        if (!yres_specified) {
-                               yres = my_atoi(&name[i+1]);
+                               yres = simple_strtol(&name[i+1], NULL, 10);
                                yres_specified = 1;
                        } else
                                goto done;
@@ -170,7 +156,7 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
                }
        }
        if (i < 0 && yres_specified) {
-               xres = my_atoi(name);
+               xres = simple_strtol(name, NULL, 10);
                res_specified = 1;
        }
 done:
@@ -694,7 +680,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
        int i;
 
        if (var->pixclock != 0) {
-               DRM_ERROR("PIXEL CLCOK SET\n");
+               DRM_ERROR("PIXEL CLOCK SET\n");
                return -EINVAL;
        }
 
index 8bf3770f294e12feb562d046a5de4ed281b9ce45..aa89d4b0b4c44e87e2726c8945895e96280dc7bb 100644 (file)
@@ -192,9 +192,7 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle)
        idr_remove(&filp->object_idr, handle);
        spin_unlock(&filp->table_lock);
 
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_handle_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_handle_unreference_unlocked(obj);
 
        return 0;
 }
@@ -325,9 +323,7 @@ again:
        }
 
 err:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(obj);
        return ret;
 }
 
@@ -358,9 +354,7 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
                return -ENOENT;
 
        ret = drm_gem_handle_create(file_priv, obj, &handle);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(obj);
        if (ret)
                return ret;
 
@@ -390,7 +384,7 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
 {
        struct drm_gem_object *obj = ptr;
 
-       drm_gem_object_handle_unreference(obj);
+       drm_gem_object_handle_unreference_unlocked(obj);
 
        return 0;
 }
@@ -403,16 +397,25 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
 void
 drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
 {
-       mutex_lock(&dev->struct_mutex);
        idr_for_each(&file_private->object_idr,
                     &drm_gem_object_release_handle, NULL);
 
        idr_destroy(&file_private->object_idr);
-       mutex_unlock(&dev->struct_mutex);
+}
+
+static void
+drm_gem_object_free_common(struct drm_gem_object *obj)
+{
+       struct drm_device *dev = obj->dev;
+       fput(obj->filp);
+       atomic_dec(&dev->object_count);
+       atomic_sub(obj->size, &dev->object_memory);
+       kfree(obj);
 }
 
 /**
  * Called after the last reference to the object has been lost.
+ * Must be called holding struct_ mutex
  *
  * Frees the object
  */
@@ -427,13 +430,39 @@ drm_gem_object_free(struct kref *kref)
        if (dev->driver->gem_free_object != NULL)
                dev->driver->gem_free_object(obj);
 
-       fput(obj->filp);
-       atomic_dec(&dev->object_count);
-       atomic_sub(obj->size, &dev->object_memory);
-       kfree(obj);
+       drm_gem_object_free_common(obj);
 }
 EXPORT_SYMBOL(drm_gem_object_free);
 
+/**
+ * Called after the last reference to the object has been lost.
+ * Must be called without holding struct_mutex
+ *
+ * Frees the object
+ */
+void
+drm_gem_object_free_unlocked(struct kref *kref)
+{
+       struct drm_gem_object *obj = (struct drm_gem_object *) kref;
+       struct drm_device *dev = obj->dev;
+
+       if (dev->driver->gem_free_object_unlocked != NULL)
+               dev->driver->gem_free_object_unlocked(obj);
+       else if (dev->driver->gem_free_object != NULL) {
+               mutex_lock(&dev->struct_mutex);
+               dev->driver->gem_free_object(obj);
+               mutex_unlock(&dev->struct_mutex);
+       }
+
+       drm_gem_object_free_common(obj);
+}
+EXPORT_SYMBOL(drm_gem_object_free_unlocked);
+
+static void drm_gem_object_ref_bug(struct kref *list_kref)
+{
+       BUG();
+}
+
 /**
  * Called after the last handle to the object has been closed
  *
@@ -458,8 +487,10 @@ drm_gem_object_handle_free(struct kref *kref)
                /*
                 * The object name held a reference to this object, drop
                 * that now.
+               *
+               * This cannot be the last reference, since the handle holds one too.
                 */
-               drm_gem_object_unreference(obj);
+               kref_put(&obj->refcount, drm_gem_object_ref_bug);
        } else
                spin_unlock(&dev->object_name_lock);
 
@@ -477,11 +508,8 @@ EXPORT_SYMBOL(drm_gem_vm_open);
 void drm_gem_vm_close(struct vm_area_struct *vma)
 {
        struct drm_gem_object *obj = vma->vm_private_data;
-       struct drm_device *dev = obj->dev;
 
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(obj);
 }
 EXPORT_SYMBOL(drm_gem_vm_close);
 
index a894ade030937c506e13322c2687b3c93b30420f..1376dfe44c952a2ec9ae9bed4ad1befa10c1bcb3 100644 (file)
@@ -162,7 +162,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       if (!IS_IRONLAKE(dev)) {
+       if (!HAS_PCH_SPLIT(dev)) {
                seq_printf(m, "Interrupt enable:    %08x\n",
                           I915_READ(IER));
                seq_printf(m, "Interrupt identity:  %08x\n",
@@ -350,6 +350,36 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
        return 0;
 }
 
+static const char *pin_flag(int pinned)
+{
+       if (pinned > 0)
+               return " P";
+       else if (pinned < 0)
+               return " p";
+       else
+               return "";
+}
+
+static const char *tiling_flag(int tiling)
+{
+       switch (tiling) {
+       default:
+       case I915_TILING_NONE: return "";
+       case I915_TILING_X: return " X";
+       case I915_TILING_Y: return " Y";
+       }
+}
+
+static const char *dirty_flag(int dirty)
+{
+       return dirty ? " dirty" : "";
+}
+
+static const char *purgeable_flag(int purgeable)
+{
+       return purgeable ? " purgeable" : "";
+}
+
 static int i915_error_state(struct seq_file *m, void *unused)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -357,6 +387,7 @@ static int i915_error_state(struct seq_file *m, void *unused)
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_error_state *error;
        unsigned long flags;
+       int i, page, offset, elt;
 
        spin_lock_irqsave(&dev_priv->error_lock, flags);
        if (!dev_priv->first_error) {
@@ -368,6 +399,7 @@ static int i915_error_state(struct seq_file *m, void *unused)
 
        seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
                   error->time.tv_usec);
+       seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device);
        seq_printf(m, "EIR: 0x%08x\n", error->eir);
        seq_printf(m, "  PGTBL_ER: 0x%08x\n", error->pgtbl_er);
        seq_printf(m, "  INSTPM: 0x%08x\n", error->instpm);
@@ -379,6 +411,59 @@ static int i915_error_state(struct seq_file *m, void *unused)
                seq_printf(m, "  INSTPS: 0x%08x\n", error->instps);
                seq_printf(m, "  INSTDONE1: 0x%08x\n", error->instdone1);
        }
+       seq_printf(m, "seqno: 0x%08x\n", error->seqno);
+
+       if (error->active_bo_count) {
+               seq_printf(m, "Buffers [%d]:\n", error->active_bo_count);
+
+               for (i = 0; i < error->active_bo_count; i++) {
+                       seq_printf(m, "  %08x %8zd %08x %08x %08x%s%s%s%s",
+                                  error->active_bo[i].gtt_offset,
+                                  error->active_bo[i].size,
+                                  error->active_bo[i].read_domains,
+                                  error->active_bo[i].write_domain,
+                                  error->active_bo[i].seqno,
+                                  pin_flag(error->active_bo[i].pinned),
+                                  tiling_flag(error->active_bo[i].tiling),
+                                  dirty_flag(error->active_bo[i].dirty),
+                                  purgeable_flag(error->active_bo[i].purgeable));
+
+                       if (error->active_bo[i].name)
+                               seq_printf(m, " (name: %d)", error->active_bo[i].name);
+                       if (error->active_bo[i].fence_reg != I915_FENCE_REG_NONE)
+                               seq_printf(m, " (fence: %d)", error->active_bo[i].fence_reg);
+
+                       seq_printf(m, "\n");
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(error->batchbuffer); i++) {
+               if (error->batchbuffer[i]) {
+                       struct drm_i915_error_object *obj = error->batchbuffer[i];
+
+                       seq_printf(m, "--- gtt_offset = 0x%08x\n", obj->gtt_offset);
+                       offset = 0;
+                       for (page = 0; page < obj->page_count; page++) {
+                               for (elt = 0; elt < PAGE_SIZE/4; elt++) {
+                                       seq_printf(m, "%08x :  %08x\n", offset, obj->pages[page][elt]);
+                                       offset += 4;
+                               }
+                       }
+               }
+       }
+
+       if (error->ringbuffer) {
+               struct drm_i915_error_object *obj = error->ringbuffer;
+
+               seq_printf(m, "--- ringbuffer = 0x%08x\n", obj->gtt_offset);
+               offset = 0;
+               for (page = 0; page < obj->page_count; page++) {
+                       for (elt = 0; elt < PAGE_SIZE/4; elt++) {
+                               seq_printf(m, "%08x :  %08x\n", offset, obj->pages[page][elt]);
+                               offset += 4;
+                       }
+               }
+       }
 
 out:
        spin_unlock_irqrestore(&dev_priv->error_lock, flags);
@@ -386,6 +471,165 @@ out:
        return 0;
 }
 
+static int i915_rstdby_delays(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u16 crstanddelay = I915_READ16(CRSTANDVID);
+
+       seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f));
+
+       return 0;
+}
+
+static int i915_cur_delayinfo(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u16 rgvswctl = I915_READ16(MEMSWCTL);
+
+       seq_printf(m, "Last command: 0x%01x\n", (rgvswctl >> 13) & 0x3);
+       seq_printf(m, "Command status: %d\n", (rgvswctl >> 12) & 1);
+       seq_printf(m, "P%d DELAY 0x%02x\n", (rgvswctl >> 8) & 0xf,
+                  rgvswctl & 0x3f);
+
+       return 0;
+}
+
+static int i915_delayfreq_table(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 delayfreq;
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
+               seq_printf(m, "P%02dVIDFREQ: 0x%08x\n", i, delayfreq);
+       }
+
+       return 0;
+}
+
+static inline int MAP_TO_MV(int map)
+{
+       return 1250 - (map * 25);
+}
+
+static int i915_inttoext_table(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 inttoext;
+       int i;
+
+       for (i = 1; i <= 32; i++) {
+               inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4);
+               seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext);
+       }
+
+       return 0;
+}
+
+static int i915_drpc_info(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 rgvmodectl = I915_READ(MEMMODECTL);
+
+       seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
+                  "yes" : "no");
+       seq_printf(m, "Boost freq: %d\n",
+                  (rgvmodectl & MEMMODE_BOOST_FREQ_MASK) >>
+                  MEMMODE_BOOST_FREQ_SHIFT);
+       seq_printf(m, "HW control enabled: %s\n",
+                  rgvmodectl & MEMMODE_HWIDLE_EN ? "yes" : "no");
+       seq_printf(m, "SW control enabled: %s\n",
+                  rgvmodectl & MEMMODE_SWMODE_EN ? "yes" : "no");
+       seq_printf(m, "Gated voltage change: %s\n",
+                  rgvmodectl & MEMMODE_RCLK_GATE ? "yes" : "no");
+       seq_printf(m, "Starting frequency: P%d\n",
+                  (rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT);
+       seq_printf(m, "Max frequency: P%d\n",
+                  (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT);
+       seq_printf(m, "Min frequency: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
+
+       return 0;
+}
+
+static int i915_fbc_status(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct drm_crtc *crtc;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       bool fbc_enabled = false;
+
+       if (!dev_priv->display.fbc_enabled) {
+               seq_printf(m, "FBC unsupported on this chipset\n");
+               return 0;
+       }
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               if (!crtc->enabled)
+                       continue;
+               if (dev_priv->display.fbc_enabled(crtc))
+                       fbc_enabled = true;
+       }
+
+       if (fbc_enabled) {
+               seq_printf(m, "FBC enabled\n");
+       } else {
+               seq_printf(m, "FBC disabled: ");
+               switch (dev_priv->no_fbc_reason) {
+               case FBC_STOLEN_TOO_SMALL:
+                       seq_printf(m, "not enough stolen memory");
+                       break;
+               case FBC_UNSUPPORTED_MODE:
+                       seq_printf(m, "mode not supported");
+                       break;
+               case FBC_MODE_TOO_LARGE:
+                       seq_printf(m, "mode too large");
+                       break;
+               case FBC_BAD_PLANE:
+                       seq_printf(m, "FBC unsupported on plane");
+                       break;
+               case FBC_NOT_TILED:
+                       seq_printf(m, "scanout buffer not tiled");
+                       break;
+               default:
+                       seq_printf(m, "unknown reason");
+               }
+               seq_printf(m, "\n");
+       }
+       return 0;
+}
+
+static int i915_sr_status(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_device *dev = node->minor->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       bool sr_enabled = false;
+
+       if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev))
+               sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
+       else if (IS_I915GM(dev))
+               sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN;
+       else if (IS_PINEVIEW(dev))
+               sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN;
+
+       seq_printf(m, "self-refresh: %s\n", sr_enabled ? "enabled" :
+                  "disabled");
+
+       return 0;
+}
+
 static int
 i915_wedged_open(struct inode *inode,
                 struct file *filp)
@@ -503,6 +747,13 @@ static struct drm_info_list i915_debugfs_list[] = {
        {"i915_ringbuffer_info", i915_ringbuffer_info, 0},
        {"i915_batchbuffers", i915_batchbuffer_info, 0},
        {"i915_error_state", i915_error_state, 0},
+       {"i915_rstdby_delays", i915_rstdby_delays, 0},
+       {"i915_cur_delayinfo", i915_cur_delayinfo, 0},
+       {"i915_delayfreq_table", i915_delayfreq_table, 0},
+       {"i915_inttoext_table", i915_inttoext_table, 0},
+       {"i915_drpc_info", i915_drpc_info, 0},
+       {"i915_fbc_status", i915_fbc_status, 0},
+       {"i915_sr_status", i915_sr_status, 0},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
index 2307f98349f7d145aec2a0d57c57a86777a3cbb6..8bfc0bbf13e658fd050457c87cfa57f3c8857e8f 100644 (file)
@@ -35,6 +35,9 @@
 #include "i915_drv.h"
 #include "i915_trace.h"
 #include <linux/vgaarb.h>
+#include <linux/acpi.h>
+#include <linux/pnp.h>
+#include <linux/vga_switcheroo.h>
 
 /* Really want an OS-independent resettable timer.  Would like to have
  * this loop run for (eg) 3 sec, but have the timer reset every time
@@ -933,6 +936,120 @@ static int i915_get_bridge_dev(struct drm_device *dev)
        return 0;
 }
 
+#define MCHBAR_I915 0x44
+#define MCHBAR_I965 0x48
+#define MCHBAR_SIZE (4*4096)
+
+#define DEVEN_REG 0x54
+#define   DEVEN_MCHBAR_EN (1 << 28)
+
+/* Allocate space for the MCH regs if needed, return nonzero on error */
+static int
+intel_alloc_mchbar_resource(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+       u32 temp_lo, temp_hi = 0;
+       u64 mchbar_addr;
+       int ret = 0;
+
+       if (IS_I965G(dev))
+               pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi);
+       pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo);
+       mchbar_addr = ((u64)temp_hi << 32) | temp_lo;
+
+       /* If ACPI doesn't have it, assume we need to allocate it ourselves */
+#ifdef CONFIG_PNP
+       if (mchbar_addr &&
+           pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) {
+               ret = 0;
+               goto out;
+       }
+#endif
+
+       /* Get some space for it */
+       ret = pci_bus_alloc_resource(dev_priv->bridge_dev->bus, &dev_priv->mch_res,
+                                    MCHBAR_SIZE, MCHBAR_SIZE,
+                                    PCIBIOS_MIN_MEM,
+                                    0,   pcibios_align_resource,
+                                    dev_priv->bridge_dev);
+       if (ret) {
+               DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret);
+               dev_priv->mch_res.start = 0;
+               goto out;
+       }
+
+       if (IS_I965G(dev))
+               pci_write_config_dword(dev_priv->bridge_dev, reg + 4,
+                                      upper_32_bits(dev_priv->mch_res.start));
+
+       pci_write_config_dword(dev_priv->bridge_dev, reg,
+                              lower_32_bits(dev_priv->mch_res.start));
+out:
+       return ret;
+}
+
+/* Setup MCHBAR if possible, return true if we should disable it again */
+static void
+intel_setup_mchbar(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+       u32 temp;
+       bool enabled;
+
+       dev_priv->mchbar_need_disable = false;
+
+       if (IS_I915G(dev) || IS_I915GM(dev)) {
+               pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
+               enabled = !!(temp & DEVEN_MCHBAR_EN);
+       } else {
+               pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
+               enabled = temp & 1;
+       }
+
+       /* If it's already enabled, don't have to do anything */
+       if (enabled)
+               return;
+
+       if (intel_alloc_mchbar_resource(dev))
+               return;
+
+       dev_priv->mchbar_need_disable = true;
+
+       /* Space is allocated or reserved, so enable it. */
+       if (IS_I915G(dev) || IS_I915GM(dev)) {
+               pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG,
+                                      temp | DEVEN_MCHBAR_EN);
+       } else {
+               pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
+               pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp | 1);
+       }
+}
+
+static void
+intel_teardown_mchbar(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
+       u32 temp;
+
+       if (dev_priv->mchbar_need_disable) {
+               if (IS_I915G(dev) || IS_I915GM(dev)) {
+                       pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
+                       temp &= ~DEVEN_MCHBAR_EN;
+                       pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, temp);
+               } else {
+                       pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
+                       temp &= ~1;
+                       pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp);
+               }
+       }
+
+       if (dev_priv->mch_res.start)
+               release_resource(&dev_priv->mch_res);
+}
+
 /**
  * i915_probe_agp - get AGP bootup configuration
  * @pdev: PCI device
@@ -978,59 +1095,123 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
         * Some of the preallocated space is taken by the GTT
         * and popup.  GTT is 1K per MB of aperture size, and popup is 4K.
         */
-       if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev))
+       if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev))
                overhead = 4096;
        else
                overhead = (*aperture_size / 1024) + 4096;
 
-       switch (tmp & INTEL_GMCH_GMS_MASK) {
-       case INTEL_855_GMCH_GMS_DISABLED:
-               DRM_ERROR("video memory is disabled\n");
-               return -1;
-       case INTEL_855_GMCH_GMS_STOLEN_1M:
-               stolen = 1 * 1024 * 1024;
-               break;
-       case INTEL_855_GMCH_GMS_STOLEN_4M:
-               stolen = 4 * 1024 * 1024;
-               break;
-       case INTEL_855_GMCH_GMS_STOLEN_8M:
-               stolen = 8 * 1024 * 1024;
-               break;
-       case INTEL_855_GMCH_GMS_STOLEN_16M:
-               stolen = 16 * 1024 * 1024;
-               break;
-       case INTEL_855_GMCH_GMS_STOLEN_32M:
-               stolen = 32 * 1024 * 1024;
-               break;
-       case INTEL_915G_GMCH_GMS_STOLEN_48M:
-               stolen = 48 * 1024 * 1024;
-               break;
-       case INTEL_915G_GMCH_GMS_STOLEN_64M:
-               stolen = 64 * 1024 * 1024;
-               break;
-       case INTEL_GMCH_GMS_STOLEN_128M:
-               stolen = 128 * 1024 * 1024;
-               break;
-       case INTEL_GMCH_GMS_STOLEN_256M:
-               stolen = 256 * 1024 * 1024;
-               break;
-       case INTEL_GMCH_GMS_STOLEN_96M:
-               stolen = 96 * 1024 * 1024;
-               break;
-       case INTEL_GMCH_GMS_STOLEN_160M:
-               stolen = 160 * 1024 * 1024;
-               break;
-       case INTEL_GMCH_GMS_STOLEN_224M:
-               stolen = 224 * 1024 * 1024;
-               break;
-       case INTEL_GMCH_GMS_STOLEN_352M:
-               stolen = 352 * 1024 * 1024;
-               break;
-       default:
-               DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
-                       tmp & INTEL_GMCH_GMS_MASK);
-               return -1;
+       if (IS_GEN6(dev)) {
+               /* SNB has memory control reg at 0x50.w */
+               pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &tmp);
+
+               switch (tmp & SNB_GMCH_GMS_STOLEN_MASK) {
+               case INTEL_855_GMCH_GMS_DISABLED:
+                       DRM_ERROR("video memory is disabled\n");
+                       return -1;
+               case SNB_GMCH_GMS_STOLEN_32M:
+                       stolen = 32 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_64M:
+                       stolen = 64 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_96M:
+                       stolen = 96 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_128M:
+                       stolen = 128 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_160M:
+                       stolen = 160 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_192M:
+                       stolen = 192 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_224M:
+                       stolen = 224 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_256M:
+                       stolen = 256 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_288M:
+                       stolen = 288 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_320M:
+                       stolen = 320 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_352M:
+                       stolen = 352 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_384M:
+                       stolen = 384 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_416M:
+                       stolen = 416 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_448M:
+                       stolen = 448 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_480M:
+                       stolen = 480 * 1024 * 1024;
+                       break;
+               case SNB_GMCH_GMS_STOLEN_512M:
+                       stolen = 512 * 1024 * 1024;
+                       break;
+               default:
+                       DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
+                                 tmp & SNB_GMCH_GMS_STOLEN_MASK);
+                       return -1;
+               }
+       } else {
+               switch (tmp & INTEL_GMCH_GMS_MASK) {
+               case INTEL_855_GMCH_GMS_DISABLED:
+                       DRM_ERROR("video memory is disabled\n");
+                       return -1;
+               case INTEL_855_GMCH_GMS_STOLEN_1M:
+                       stolen = 1 * 1024 * 1024;
+                       break;
+               case INTEL_855_GMCH_GMS_STOLEN_4M:
+                       stolen = 4 * 1024 * 1024;
+                       break;
+               case INTEL_855_GMCH_GMS_STOLEN_8M:
+                       stolen = 8 * 1024 * 1024;
+                       break;
+               case INTEL_855_GMCH_GMS_STOLEN_16M:
+                       stolen = 16 * 1024 * 1024;
+                       break;
+               case INTEL_855_GMCH_GMS_STOLEN_32M:
+                       stolen = 32 * 1024 * 1024;
+                       break;
+               case INTEL_915G_GMCH_GMS_STOLEN_48M:
+                       stolen = 48 * 1024 * 1024;
+                       break;
+               case INTEL_915G_GMCH_GMS_STOLEN_64M:
+                       stolen = 64 * 1024 * 1024;
+                       break;
+               case INTEL_GMCH_GMS_STOLEN_128M:
+                       stolen = 128 * 1024 * 1024;
+                       break;
+               case INTEL_GMCH_GMS_STOLEN_256M:
+                       stolen = 256 * 1024 * 1024;
+                       break;
+               case INTEL_GMCH_GMS_STOLEN_96M:
+                       stolen = 96 * 1024 * 1024;
+                       break;
+               case INTEL_GMCH_GMS_STOLEN_160M:
+                       stolen = 160 * 1024 * 1024;
+                       break;
+               case INTEL_GMCH_GMS_STOLEN_224M:
+                       stolen = 224 * 1024 * 1024;
+                       break;
+               case INTEL_GMCH_GMS_STOLEN_352M:
+                       stolen = 352 * 1024 * 1024;
+                       break;
+               default:
+                       DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
+                                 tmp & INTEL_GMCH_GMS_MASK);
+                       return -1;
+               }
        }
+
        *preallocated_size = stolen - overhead;
        *start = overhead;
 
@@ -1064,7 +1245,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
        int gtt_offset, gtt_size;
 
        if (IS_I965G(dev)) {
-               if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
+               if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) {
                        gtt_offset = 2*1024*1024;
                        gtt_size = 2*1024*1024;
                } else {
@@ -1133,6 +1314,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
        /* Leave 1M for line length buffer & misc. */
        compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0);
        if (!compressed_fb) {
+               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
                i915_warn_stolen(dev);
                return;
        }
@@ -1140,6 +1322,7 @@ static void i915_setup_compression(struct drm_device *dev, int size)
        compressed_fb = drm_mm_get_block(compressed_fb, size, 4096);
        if (!compressed_fb) {
                i915_warn_stolen(dev);
+               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
                return;
        }
 
@@ -1199,6 +1382,32 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state)
                return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
 }
 
+static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+       if (state == VGA_SWITCHEROO_ON) {
+               printk(KERN_INFO "i915: switched off\n");
+               /* i915 resume handler doesn't set to D0 */
+               pci_set_power_state(dev->pdev, PCI_D0);
+               i915_resume(dev);
+       } else {
+               printk(KERN_ERR "i915: switched off\n");
+               i915_suspend(dev, pmm);
+       }
+}
+
+static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
+}
+
 static int i915_load_modeset_init(struct drm_device *dev,
                                  unsigned long prealloc_start,
                                  unsigned long prealloc_size,
@@ -1260,6 +1469,12 @@ static int i915_load_modeset_init(struct drm_device *dev,
        if (ret)
                goto destroy_ringbuffer;
 
+       ret = vga_switcheroo_register_client(dev->pdev,
+                                            i915_switcheroo_set_state,
+                                            i915_switcheroo_can_switch);
+       if (ret)
+               goto destroy_ringbuffer;
+
        intel_modeset_init(dev);
 
        ret = drm_irq_install(dev);
@@ -1281,7 +1496,9 @@ static int i915_load_modeset_init(struct drm_device *dev,
        return 0;
 
 destroy_ringbuffer:
+       mutex_lock(&dev->struct_mutex);
        i915_gem_cleanup_ringbuffer(dev);
+       mutex_unlock(&dev->struct_mutex);
 out:
        return ret;
 }
@@ -1445,11 +1662,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
-       if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
+       if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) {
                dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
                dev->driver->get_vblank_counter = gm45_get_vblank_counter;
        }
 
+       /* Try to make sure MCHBAR is enabled before poking at it */
+       intel_setup_mchbar(dev);
+
        i915_gem_load(dev);
 
        /* Init HWS */
@@ -1523,6 +1743,8 @@ int i915_driver_unload(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       i915_destroy_error_state(dev);
+
        destroy_workqueue(dev_priv->wq);
        del_timer_sync(&dev_priv->hangcheck_timer);
 
@@ -1544,6 +1766,7 @@ int i915_driver_unload(struct drm_device *dev)
                        dev_priv->child_dev_num = 0;
                }
                drm_irq_uninstall(dev);
+               vga_switcheroo_unregister_client(dev->pdev);
                vga_client_register(dev->pdev, NULL, NULL, NULL);
        }
 
@@ -1569,6 +1792,8 @@ int i915_driver_unload(struct drm_device *dev)
                intel_cleanup_overlay(dev);
        }
 
+       intel_teardown_mchbar(dev);
+
        pci_dev_put(dev_priv->bridge_dev);
        kfree(dev->dev_private);
 
@@ -1611,6 +1836,7 @@ void i915_driver_lastclose(struct drm_device * dev)
 
        if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
                drm_fb_helper_restore();
+               vga_switcheroo_process_delayed_switch();
                return;
        }
 
index cf4cb3e9a0c22a5a244350db95f8d846b05e3fdb..1b2e95455c05d0cce04d17483c7bd4ff9f218fe0 100644 (file)
@@ -49,6 +49,7 @@ unsigned int i915_lvds_downclock = 0;
 module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
 
 static struct drm_driver driver;
+extern int intel_agp_enabled;
 
 #define INTEL_VGA_DEVICE(id, info) {           \
        .class = PCI_CLASS_DISPLAY_VGA << 8,    \
@@ -136,6 +137,16 @@ const static struct intel_device_info intel_ironlake_m_info = {
        .has_hotplug = 1,
 };
 
+const static struct intel_device_info intel_sandybridge_d_info = {
+       .is_i965g = 1, .is_i9xx = 1, .need_gfx_hws = 1,
+       .has_hotplug = 1,
+};
+
+const static struct intel_device_info intel_sandybridge_m_info = {
+       .is_i965g = 1, .is_mobile = 1, .is_i9xx = 1, .need_gfx_hws = 1,
+       .has_hotplug = 1,
+};
+
 const static struct pci_device_id pciidlist[] = {
        INTEL_VGA_DEVICE(0x3577, &intel_i830_info),
        INTEL_VGA_DEVICE(0x2562, &intel_845g_info),
@@ -167,6 +178,8 @@ const static struct pci_device_id pciidlist[] = {
        INTEL_VGA_DEVICE(0xa011, &intel_pineview_info),
        INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info),
        INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info),
+       INTEL_VGA_DEVICE(0x0102, &intel_sandybridge_d_info),
+       INTEL_VGA_DEVICE(0x0106, &intel_sandybridge_m_info),
        {0, 0, 0}
 };
 
@@ -201,7 +214,7 @@ static int i915_drm_freeze(struct drm_device *dev)
        return 0;
 }
 
-static int i915_suspend(struct drm_device *dev, pm_message_t state)
+int i915_suspend(struct drm_device *dev, pm_message_t state)
 {
        int error;
 
@@ -255,7 +268,7 @@ static int i915_drm_thaw(struct drm_device *dev)
        return error;
 }
 
-static int i915_resume(struct drm_device *dev)
+int i915_resume(struct drm_device *dev)
 {
        if (pci_enable_device(dev->pdev))
                return -EIO;
@@ -546,6 +559,11 @@ static struct drm_driver driver = {
 
 static int __init i915_init(void)
 {
+       if (!intel_agp_enabled) {
+               DRM_ERROR("drm/i915 can't work without intel_agp module!\n");
+               return -ENODEV;
+       }
+
        driver.num_ioctls = i915_max_ioctl;
 
        i915_gem_shrinker_init();
@@ -571,6 +589,11 @@ static int __init i915_init(void)
                driver.driver_features &= ~DRIVER_MODESET;
 #endif
 
+       if (!(driver.driver_features & DRIVER_MODESET)) {
+               driver.suspend = i915_suspend;
+               driver.resume = i915_resume;
+       }
+
        return drm_init(&driver);
 }
 
index b99b6a841d9506b1782562e825b383ac650aaf9c..979439cfb827998f9301a1bcee3626957828c268 100644 (file)
@@ -150,7 +150,27 @@ struct drm_i915_error_state {
        u32 instps;
        u32 instdone1;
        u32 seqno;
+       u64 bbaddr;
        struct timeval time;
+       struct drm_i915_error_object {
+               int page_count;
+               u32 gtt_offset;
+               u32 *pages[0];
+       } *ringbuffer, *batchbuffer[2];
+       struct drm_i915_error_buffer {
+               size_t size;
+               u32 name;
+               u32 seqno;
+               u32 gtt_offset;
+               u32 read_domains;
+               u32 write_domain;
+               u32 fence_reg;
+               s32 pinned:2;
+               u32 tiling:2;
+               u32 dirty:1;
+               u32 purgeable:1;
+       } *active_bo;
+       u32 active_bo_count;
 };
 
 struct drm_i915_display_funcs {
@@ -192,6 +212,14 @@ struct intel_device_info {
        u8 cursor_needs_physical : 1;
 };
 
+enum no_fbc_reason {
+       FBC_STOLEN_TOO_SMALL, /* not enough space to hold compressed buffers */
+       FBC_UNSUPPORTED_MODE, /* interlace or doublescanned mode */
+       FBC_MODE_TOO_LARGE, /* mode too large for compression */
+       FBC_BAD_PLANE, /* fbc not supported on plane */
+       FBC_NOT_TILED, /* buffer not tiled */
+};
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
 
@@ -452,6 +480,7 @@ typedef struct drm_i915_private {
        u32 savePIPEB_DATA_N1;
        u32 savePIPEB_LINK_M1;
        u32 savePIPEB_LINK_N1;
+       u32 saveMCHBAR_RENDER_STANDBY;
 
        struct {
                struct drm_mm gtt_space;
@@ -590,6 +619,14 @@ typedef struct drm_i915_private {
        int child_dev_num;
        struct child_device_config *child_dev;
        struct drm_connector *int_lvds_connector;
+
+       bool mchbar_need_disable;
+
+       u8 cur_delay;
+       u8 min_delay;
+       u8 max_delay;
+
+       enum no_fbc_reason no_fbc_reason;
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
@@ -736,6 +773,8 @@ extern unsigned int i915_fbpercrtc;
 extern unsigned int i915_powersave;
 extern unsigned int i915_lvds_downclock;
 
+extern int i915_suspend(struct drm_device *dev, pm_message_t state);
+extern int i915_resume(struct drm_device *dev);
 extern void i915_save_display(struct drm_device *dev);
 extern void i915_restore_display(struct drm_device *dev);
 extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
@@ -761,6 +800,7 @@ extern int i965_reset(struct drm_device *dev, u8 flags);
 
 /* i915_irq.c */
 void i915_hangcheck_elapsed(unsigned long data);
+void i915_destroy_error_state(struct drm_device *dev);
 extern int i915_irq_emit(struct drm_device *dev, void *data,
                         struct drm_file *file_priv);
 extern int i915_irq_wait(struct drm_device *dev, void *data,
@@ -897,7 +937,8 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj);
 void i915_gem_object_save_bit_17_swizzle(struct drm_gem_object *obj);
 bool i915_tiling_ok(struct drm_device *dev, int stride, int size,
                    int tiling_mode);
-bool i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj);
+bool i915_gem_object_fence_offset_ok(struct drm_gem_object *obj,
+                                    int tiling_mode);
 
 /* i915_gem_debug.c */
 void i915_gem_dump_object(struct drm_gem_object *obj, int len,
@@ -1026,7 +1067,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define IS_845G(dev)           ((dev)->pci_device == 0x2562)
 #define IS_I85X(dev)           ((dev)->pci_device == 0x3582)
 #define IS_I865G(dev)          ((dev)->pci_device == 0x2572)
-#define IS_I8XX(dev)           (INTEL_INFO(dev)->is_i8xx)
+#define IS_GEN2(dev)           (INTEL_INFO(dev)->is_i8xx)
 #define IS_I915G(dev)          (INTEL_INFO(dev)->is_i915g)
 #define IS_I915GM(dev)         ((dev)->pci_device == 0x2592)
 #define IS_I945G(dev)          ((dev)->pci_device == 0x2772)
@@ -1045,8 +1086,29 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define IS_I9XX(dev)           (INTEL_INFO(dev)->is_i9xx)
 #define IS_MOBILE(dev)         (INTEL_INFO(dev)->is_mobile)
 
+#define IS_GEN3(dev)   (IS_I915G(dev) ||                       \
+                        IS_I915GM(dev) ||                      \
+                        IS_I945G(dev) ||                       \
+                        IS_I945GM(dev) ||                      \
+                        IS_G33(dev) || \
+                        IS_PINEVIEW(dev))
+#define IS_GEN4(dev)   ((dev)->pci_device == 0x2972 ||         \
+                        (dev)->pci_device == 0x2982 ||         \
+                        (dev)->pci_device == 0x2992 ||         \
+                        (dev)->pci_device == 0x29A2 ||         \
+                        (dev)->pci_device == 0x2A02 ||         \
+                        (dev)->pci_device == 0x2A12 ||         \
+                        (dev)->pci_device == 0x2E02 ||         \
+                        (dev)->pci_device == 0x2E12 ||         \
+                        (dev)->pci_device == 0x2E22 ||         \
+                        (dev)->pci_device == 0x2E32 ||         \
+                        (dev)->pci_device == 0x2A42 ||         \
+                        (dev)->pci_device == 0x2E42)
+
 #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
 
+#define IS_GEN6(dev)   ((dev)->pci_device == 0x0102)
+
 /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
  * rows, which changed the alignment requirements and fence programming.
  */
@@ -1067,6 +1129,9 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
 #define I915_HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6)
 
+#define HAS_PCH_SPLIT(dev) (IS_IRONLAKE(dev) ||        \
+                           IS_GEN6(dev))
+
 #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
 
 #endif
index ec8a0d7ffa3990ea9f7ded8871f3ff384b2bf24d..fba37e9f775d6fefdacaacfd0bd0868f6075328f 100644 (file)
@@ -128,9 +128,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
                return -ENOMEM;
 
        ret = drm_gem_handle_create(file_priv, obj, &handle);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_handle_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_handle_unreference_unlocked(obj);
 
        if (ret)
                return ret;
@@ -488,7 +486,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
         */
        if (args->offset > obj->size || args->size > obj->size ||
            args->offset + args->size > obj->size) {
-               drm_gem_object_unreference(obj);
+               drm_gem_object_unreference_unlocked(obj);
                return -EINVAL;
        }
 
@@ -501,7 +499,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
                                                        file_priv);
        }
 
-       drm_gem_object_unreference(obj);
+       drm_gem_object_unreference_unlocked(obj);
 
        return ret;
 }
@@ -961,7 +959,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
         */
        if (args->offset > obj->size || args->size > obj->size ||
            args->offset + args->size > obj->size) {
-               drm_gem_object_unreference(obj);
+               drm_gem_object_unreference_unlocked(obj);
                return -EINVAL;
        }
 
@@ -995,7 +993,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
                DRM_INFO("pwrite failed %d\n", ret);
 #endif
 
-       drm_gem_object_unreference(obj);
+       drm_gem_object_unreference_unlocked(obj);
 
        return ret;
 }
@@ -1138,9 +1136,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                       PROT_READ | PROT_WRITE, MAP_SHARED,
                       args->offset);
        up_write(&current->mm->mmap_sem);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(obj);
        if (IS_ERR((void *)addr))
                return addr;
 
@@ -1562,6 +1558,38 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
        i915_verify_inactive(dev, __FILE__, __LINE__);
 }
 
+static void
+i915_gem_process_flushing_list(struct drm_device *dev,
+                              uint32_t flush_domains, uint32_t seqno)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj_priv, *next;
+
+       list_for_each_entry_safe(obj_priv, next,
+                                &dev_priv->mm.gpu_write_list,
+                                gpu_write_list) {
+               struct drm_gem_object *obj = obj_priv->obj;
+
+               if ((obj->write_domain & flush_domains) ==
+                   obj->write_domain) {
+                       uint32_t old_write_domain = obj->write_domain;
+
+                       obj->write_domain = 0;
+                       list_del_init(&obj_priv->gpu_write_list);
+                       i915_gem_object_move_to_active(obj, seqno);
+
+                       /* update the fence lru list */
+                       if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
+                               list_move_tail(&obj_priv->fence_list,
+                                               &dev_priv->mm.fence_list);
+
+                       trace_i915_gem_object_change_domain(obj,
+                                                           obj->read_domains,
+                                                           old_write_domain);
+               }
+       }
+}
+
 /**
  * Creates a new sequence number, emitting a write of it to the status page
  * plus an interrupt, which will trigger i915_user_interrupt_handler.
@@ -1620,29 +1648,8 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
        /* Associate any objects on the flushing list matching the write
         * domain we're flushing with our flush.
         */
-       if (flush_domains != 0) {
-               struct drm_i915_gem_object *obj_priv, *next;
-
-               list_for_each_entry_safe(obj_priv, next,
-                                        &dev_priv->mm.gpu_write_list,
-                                        gpu_write_list) {
-                       struct drm_gem_object *obj = obj_priv->obj;
-
-                       if ((obj->write_domain & flush_domains) ==
-                           obj->write_domain) {
-                               uint32_t old_write_domain = obj->write_domain;
-
-                               obj->write_domain = 0;
-                               list_del_init(&obj_priv->gpu_write_list);
-                               i915_gem_object_move_to_active(obj, seqno);
-
-                               trace_i915_gem_object_change_domain(obj,
-                                                                   obj->read_domains,
-                                                                   old_write_domain);
-                       }
-               }
-
-       }
+       if (flush_domains != 0) 
+               i915_gem_process_flushing_list(dev, flush_domains, seqno);
 
        if (!dev_priv->mm.suspended) {
                mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
@@ -1822,7 +1829,7 @@ i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible)
                return -EIO;
 
        if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        ier = I915_READ(DEIER) | I915_READ(GTIER);
                else
                        ier = I915_READ(IER);
@@ -1991,6 +1998,7 @@ int
 i915_gem_object_unbind(struct drm_gem_object *obj)
 {
        struct drm_device *dev = obj->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
        int ret = 0;
 
@@ -2046,8 +2054,10 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
        }
 
        /* Remove ourselves from the LRU list if present. */
+       spin_lock(&dev_priv->mm.active_list_lock);
        if (!list_empty(&obj_priv->list))
                list_del_init(&obj_priv->list);
+       spin_unlock(&dev_priv->mm.active_list_lock);
 
        if (i915_gem_object_is_purgeable(obj_priv))
                i915_gem_object_truncate(obj);
@@ -2084,12 +2094,35 @@ i915_gem_find_inactive_object(struct drm_device *dev, int min_size)
        return best ? best : first;
 }
 
+static int
+i915_gpu_idle(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       bool lists_empty;
+       uint32_t seqno;
+
+       spin_lock(&dev_priv->mm.active_list_lock);
+       lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
+                     list_empty(&dev_priv->mm.active_list);
+       spin_unlock(&dev_priv->mm.active_list_lock);
+
+       if (lists_empty)
+               return 0;
+
+       /* Flush everything onto the inactive list. */
+       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
+       if (seqno == 0)
+               return -ENOMEM;
+
+       return i915_wait_request(dev, seqno);
+}
+
 static int
 i915_gem_evict_everything(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        int ret;
-       uint32_t seqno;
        bool lists_empty;
 
        spin_lock(&dev_priv->mm.active_list_lock);
@@ -2102,12 +2135,7 @@ i915_gem_evict_everything(struct drm_device *dev)
                return -ENOSPC;
 
        /* Flush everything (on to the inactive lists) and evict */
-       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
-       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
-       if (seqno == 0)
-               return -ENOMEM;
-
-       ret = i915_wait_request(dev, seqno);
+       ret = i915_gpu_idle(dev);
        if (ret)
                return ret;
 
@@ -2265,6 +2293,28 @@ i915_gem_object_get_pages(struct drm_gem_object *obj,
        return 0;
 }
 
+static void sandybridge_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+       struct drm_gem_object *obj = reg->obj;
+       struct drm_device *dev = obj->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj_priv = obj->driver_private;
+       int regnum = obj_priv->fence_reg;
+       uint64_t val;
+
+       val = (uint64_t)((obj_priv->gtt_offset + obj->size - 4096) &
+                   0xfffff000) << 32;
+       val |= obj_priv->gtt_offset & 0xfffff000;
+       val |= (uint64_t)((obj_priv->stride / 128) - 1) <<
+               SANDYBRIDGE_FENCE_PITCH_SHIFT;
+
+       if (obj_priv->tiling_mode == I915_TILING_Y)
+               val |= 1 << I965_FENCE_TILING_Y_SHIFT;
+       val |= I965_FENCE_REG_VALID;
+
+       I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (regnum * 8), val);
+}
+
 static void i965_write_fence_reg(struct drm_i915_fence_reg *reg)
 {
        struct drm_gem_object *obj = reg->obj;
@@ -2361,6 +2411,58 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
        I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
 }
 
+static int i915_find_fence_reg(struct drm_device *dev)
+{
+       struct drm_i915_fence_reg *reg = NULL;
+       struct drm_i915_gem_object *obj_priv = NULL;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_gem_object *obj = NULL;
+       int i, avail, ret;
+
+       /* First try to find a free reg */
+       avail = 0;
+       for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
+               reg = &dev_priv->fence_regs[i];
+               if (!reg->obj)
+                       return i;
+
+               obj_priv = reg->obj->driver_private;
+               if (!obj_priv->pin_count)
+                   avail++;
+       }
+
+       if (avail == 0)
+               return -ENOSPC;
+
+       /* None available, try to steal one or wait for a user to finish */
+       i = I915_FENCE_REG_NONE;
+       list_for_each_entry(obj_priv, &dev_priv->mm.fence_list,
+                           fence_list) {
+               obj = obj_priv->obj;
+
+               if (obj_priv->pin_count)
+                       continue;
+
+               /* found one! */
+               i = obj_priv->fence_reg;
+               break;
+       }
+
+       BUG_ON(i == I915_FENCE_REG_NONE);
+
+       /* We only have a reference on obj from the active list. put_fence_reg
+        * might drop that one, causing a use-after-free in it. So hold a
+        * private reference to obj like the other callers of put_fence_reg
+        * (set_tiling ioctl) do. */
+       drm_gem_object_reference(obj);
+       ret = i915_gem_object_put_fence_reg(obj);
+       drm_gem_object_unreference(obj);
+       if (ret != 0)
+               return ret;
+
+       return i;
+}
+
 /**
  * i915_gem_object_get_fence_reg - set up a fence reg for an object
  * @obj: object to map through a fence reg
@@ -2381,8 +2483,7 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
        struct drm_i915_fence_reg *reg = NULL;
-       struct drm_i915_gem_object *old_obj_priv = NULL;
-       int i, ret, avail;
+       int ret;
 
        /* Just update our place in the LRU if our fence is getting used. */
        if (obj_priv->fence_reg != I915_FENCE_REG_NONE) {
@@ -2410,86 +2511,27 @@ i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
                break;
        }
 
-       /* First try to find a free reg */
-       avail = 0;
-       for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
-               reg = &dev_priv->fence_regs[i];
-               if (!reg->obj)
-                       break;
-
-               old_obj_priv = reg->obj->driver_private;
-               if (!old_obj_priv->pin_count)
-                   avail++;
-       }
-
-       /* None available, try to steal one or wait for a user to finish */
-       if (i == dev_priv->num_fence_regs) {
-               struct drm_gem_object *old_obj = NULL;
-
-               if (avail == 0)
-                       return -ENOSPC;
-
-               list_for_each_entry(old_obj_priv, &dev_priv->mm.fence_list,
-                                   fence_list) {
-                       old_obj = old_obj_priv->obj;
-
-                       if (old_obj_priv->pin_count)
-                               continue;
-
-                       /* Take a reference, as otherwise the wait_rendering
-                        * below may cause the object to get freed out from
-                        * under us.
-                        */
-                       drm_gem_object_reference(old_obj);
-
-                       /* i915 uses fences for GPU access to tiled buffers */
-                       if (IS_I965G(dev) || !old_obj_priv->active)
-                               break;
-
-                       /* This brings the object to the head of the LRU if it
-                        * had been written to.  The only way this should
-                        * result in us waiting longer than the expected
-                        * optimal amount of time is if there was a
-                        * fence-using buffer later that was read-only.
-                        */
-                       i915_gem_object_flush_gpu_write_domain(old_obj);
-                       ret = i915_gem_object_wait_rendering(old_obj);
-                       if (ret != 0) {
-                               drm_gem_object_unreference(old_obj);
-                               return ret;
-                       }
-
-                       break;
-               }
-
-               /*
-                * Zap this virtual mapping so we can set up a fence again
-                * for this object next time we need it.
-                */
-               i915_gem_release_mmap(old_obj);
-
-               i = old_obj_priv->fence_reg;
-               reg = &dev_priv->fence_regs[i];
-
-               old_obj_priv->fence_reg = I915_FENCE_REG_NONE;
-               list_del_init(&old_obj_priv->fence_list);
-
-               drm_gem_object_unreference(old_obj);
-       }
+       ret = i915_find_fence_reg(dev);
+       if (ret < 0)
+               return ret;
 
-       obj_priv->fence_reg = i;
+       obj_priv->fence_reg = ret;
+       reg = &dev_priv->fence_regs[obj_priv->fence_reg];
        list_add_tail(&obj_priv->fence_list, &dev_priv->mm.fence_list);
 
        reg->obj = obj;
 
-       if (IS_I965G(dev))
+       if (IS_GEN6(dev))
+               sandybridge_write_fence_reg(reg);
+       else if (IS_I965G(dev))
                i965_write_fence_reg(reg);
        else if (IS_I9XX(dev))
                i915_write_fence_reg(reg);
        else
                i830_write_fence_reg(reg);
 
-       trace_i915_gem_object_get_fence(obj, i, obj_priv->tiling_mode);
+       trace_i915_gem_object_get_fence(obj, obj_priv->fence_reg,
+                       obj_priv->tiling_mode);
 
        return 0;
 }
@@ -2508,9 +2550,12 @@ i915_gem_clear_fence_reg(struct drm_gem_object *obj)
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj_priv = obj->driver_private;
 
-       if (IS_I965G(dev))
+       if (IS_GEN6(dev)) {
+               I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 +
+                            (obj_priv->fence_reg * 8), 0);
+       } else if (IS_I965G(dev)) {
                I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0);
-       else {
+       else {
                uint32_t fence_reg;
 
                if (obj_priv->fence_reg < 8)
@@ -2544,6 +2589,12 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj)
        if (obj_priv->fence_reg == I915_FENCE_REG_NONE)
                return 0;
 
+       /* If we've changed tiling, GTT-mappings of the object
+        * need to re-fault to ensure that the correct fence register
+        * setup is in place.
+        */
+       i915_gem_release_mmap(obj);
+
        /* On the i915, GPU access to tiled buffers is via a fence,
         * therefore we must wait for any outstanding access to complete
         * before clearing the fence.
@@ -2552,12 +2603,12 @@ i915_gem_object_put_fence_reg(struct drm_gem_object *obj)
                int ret;
 
                i915_gem_object_flush_gpu_write_domain(obj);
-               i915_gem_object_flush_gtt_write_domain(obj);
                ret = i915_gem_object_wait_rendering(obj);
                if (ret != 0)
                        return ret;
        }
 
+       i915_gem_object_flush_gtt_write_domain(obj);
        i915_gem_clear_fence_reg (obj);
 
        return 0;
@@ -2697,7 +2748,6 @@ static void
 i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
 {
        struct drm_device *dev = obj->dev;
-       uint32_t seqno;
        uint32_t old_write_domain;
 
        if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0)
@@ -2706,9 +2756,8 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
        /* Queue the GPU write cache flushing we need. */
        old_write_domain = obj->write_domain;
        i915_gem_flush(dev, 0, obj->write_domain);
-       seqno = i915_add_request(dev, NULL, obj->write_domain);
+       (void) i915_add_request(dev, NULL, obj->write_domain);
        BUG_ON(obj->write_domain);
-       i915_gem_object_move_to_active(obj, seqno);
 
        trace_i915_gem_object_change_domain(obj,
                                            obj->read_domains,
@@ -3247,7 +3296,8 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                     obj_priv->tiling_mode != I915_TILING_NONE;
 
        /* Check fence reg constraints and rebind if necessary */
-       if (need_fence && !i915_obj_fenceable(dev, obj))
+       if (need_fence && !i915_gem_object_fence_offset_ok(obj,
+           obj_priv->tiling_mode))
                i915_gem_object_unbind(obj);
 
        /* Choose the GTT offset for our buffer and put it there. */
@@ -3317,6 +3367,16 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
                }
 
                /* Validate that the target is in a valid r/w GPU domain */
+               if (reloc->write_domain & (reloc->write_domain - 1)) {
+                       DRM_ERROR("reloc with multiple write domains: "
+                                 "obj %p target %d offset %d "
+                                 "read %08x write %08x",
+                                 obj, reloc->target_handle,
+                                 (int) reloc->offset,
+                                 reloc->read_domains,
+                                 reloc->write_domain);
+                       return -EINVAL;
+               }
                if (reloc->write_domain & I915_GEM_DOMAIN_CPU ||
                    reloc->read_domains & I915_GEM_DOMAIN_CPU) {
                        DRM_ERROR("reloc with read/write CPU domains: "
@@ -4445,8 +4505,7 @@ int
 i915_gem_idle(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       uint32_t seqno, cur_seqno, last_seqno;
-       int stuck, ret;
+       int ret;
 
        mutex_lock(&dev->struct_mutex);
 
@@ -4455,115 +4514,36 @@ i915_gem_idle(struct drm_device *dev)
                return 0;
        }
 
-       /* Hack!  Don't let anybody do execbuf while we don't control the chip.
-        * We need to replace this with a semaphore, or something.
-        */
-       dev_priv->mm.suspended = 1;
-       del_timer(&dev_priv->hangcheck_timer);
-
-       /* Cancel the retire work handler, wait for it to finish if running
-        */
-       mutex_unlock(&dev->struct_mutex);
-       cancel_delayed_work_sync(&dev_priv->mm.retire_work);
-       mutex_lock(&dev->struct_mutex);
-
-       i915_kernel_lost_context(dev);
-
-       /* Flush the GPU along with all non-CPU write domains
-        */
-       i915_gem_flush(dev, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
-       seqno = i915_add_request(dev, NULL, I915_GEM_GPU_DOMAINS);
-
-       if (seqno == 0) {
+       ret = i915_gpu_idle(dev);
+       if (ret) {
                mutex_unlock(&dev->struct_mutex);
-               return -ENOMEM;
+               return ret;
        }
 
-       dev_priv->mm.waiting_gem_seqno = seqno;
-       last_seqno = 0;
-       stuck = 0;
-       for (;;) {
-               cur_seqno = i915_get_gem_seqno(dev);
-               if (i915_seqno_passed(cur_seqno, seqno))
-                       break;
-               if (last_seqno == cur_seqno) {
-                       if (stuck++ > 100) {
-                               DRM_ERROR("hardware wedged\n");
-                               atomic_set(&dev_priv->mm.wedged, 1);
-                               DRM_WAKEUP(&dev_priv->irq_queue);
-                               break;
-                       }
+       /* Under UMS, be paranoid and evict. */
+       if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
+               ret = i915_gem_evict_from_inactive_list(dev);
+               if (ret) {
+                       mutex_unlock(&dev->struct_mutex);
+                       return ret;
                }
-               msleep(10);
-               last_seqno = cur_seqno;
-       }
-       dev_priv->mm.waiting_gem_seqno = 0;
-
-       i915_gem_retire_requests(dev);
-
-       spin_lock(&dev_priv->mm.active_list_lock);
-       if (!atomic_read(&dev_priv->mm.wedged)) {
-               /* Active and flushing should now be empty as we've
-                * waited for a sequence higher than any pending execbuffer
-                */
-               WARN_ON(!list_empty(&dev_priv->mm.active_list));
-               WARN_ON(!list_empty(&dev_priv->mm.flushing_list));
-               /* Request should now be empty as we've also waited
-                * for the last request in the list
-                */
-               WARN_ON(!list_empty(&dev_priv->mm.request_list));
        }
 
-       /* Empty the active and flushing lists to inactive.  If there's
-        * anything left at this point, it means that we're wedged and
-        * nothing good's going to happen by leaving them there.  So strip
-        * the GPU domains and just stuff them onto inactive.
+       /* Hack!  Don't let anybody do execbuf while we don't control the chip.
+        * We need to replace this with a semaphore, or something.
+        * And not confound mm.suspended!
         */
-       while (!list_empty(&dev_priv->mm.active_list)) {
-               struct drm_gem_object *obj;
-               uint32_t old_write_domain;
-
-               obj = list_first_entry(&dev_priv->mm.active_list,
-                                      struct drm_i915_gem_object,
-                                      list)->obj;
-               old_write_domain = obj->write_domain;
-               obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
-               i915_gem_object_move_to_inactive(obj);
-
-               trace_i915_gem_object_change_domain(obj,
-                                                   obj->read_domains,
-                                                   old_write_domain);
-       }
-       spin_unlock(&dev_priv->mm.active_list_lock);
-
-       while (!list_empty(&dev_priv->mm.flushing_list)) {
-               struct drm_gem_object *obj;
-               uint32_t old_write_domain;
-
-               obj = list_first_entry(&dev_priv->mm.flushing_list,
-                                      struct drm_i915_gem_object,
-                                      list)->obj;
-               old_write_domain = obj->write_domain;
-               obj->write_domain &= ~I915_GEM_GPU_DOMAINS;
-               i915_gem_object_move_to_inactive(obj);
-
-               trace_i915_gem_object_change_domain(obj,
-                                                   obj->read_domains,
-                                                   old_write_domain);
-       }
-
-
-       /* Move all inactive buffers out of the GTT. */
-       ret = i915_gem_evict_from_inactive_list(dev);
-       WARN_ON(!list_empty(&dev_priv->mm.inactive_list));
-       if (ret) {
-               mutex_unlock(&dev->struct_mutex);
-               return ret;
-       }
+       dev_priv->mm.suspended = 1;
+       del_timer(&dev_priv->hangcheck_timer);
 
+       i915_kernel_lost_context(dev);
        i915_gem_cleanup_ringbuffer(dev);
+
        mutex_unlock(&dev->struct_mutex);
 
+       /* Cancel the retire work handler, which should be idle now. */
+       cancel_delayed_work_sync(&dev_priv->mm.retire_work);
+
        return 0;
 }
 
@@ -4607,8 +4587,13 @@ i915_gem_init_hws(struct drm_device *dev)
        }
        dev_priv->hws_obj = obj;
        memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
-       I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
-       I915_READ(HWS_PGA); /* posting read */
+       if (IS_GEN6(dev)) {
+               I915_WRITE(HWS_PGA_GEN6, dev_priv->status_gfx_addr);
+               I915_READ(HWS_PGA_GEN6); /* posting read */
+       } else {
+               I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
+               I915_READ(HWS_PGA); /* posting read */
+       }
        DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
 
        return 0;
@@ -4850,7 +4835,8 @@ i915_gem_load(struct drm_device *dev)
        spin_unlock(&shrink_list_lock);
 
        /* Old X drivers will take 0-2 for front, back, depth buffers */
-       dev_priv->fence_reg_start = 3;
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               dev_priv->fence_reg_start = 3;
 
        if (IS_I965G(dev) || IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
                dev_priv->num_fence_regs = 16;
index df278b2685bff16c4da8b1a26eb6982149fce4e1..b5c55d88ff76f260f87a0dc64b140739dcc7ec4f 100644 (file)
@@ -25,8 +25,6 @@
  *
  */
 
-#include <linux/acpi.h>
-#include <linux/pnp.h>
 #include "linux/string.h"
 #include "linux/bitops.h"
 #include "drmP.h"
  * to match what the GPU expects.
  */
 
-#define MCHBAR_I915 0x44
-#define MCHBAR_I965 0x48
-#define MCHBAR_SIZE (4*4096)
-
-#define DEVEN_REG 0x54
-#define   DEVEN_MCHBAR_EN (1 << 28)
-
-/* Allocate space for the MCH regs if needed, return nonzero on error */
-static int
-intel_alloc_mchbar_resource(struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       int reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
-       u32 temp_lo, temp_hi = 0;
-       u64 mchbar_addr;
-       int ret = 0;
-
-       if (IS_I965G(dev))
-               pci_read_config_dword(dev_priv->bridge_dev, reg + 4, &temp_hi);
-       pci_read_config_dword(dev_priv->bridge_dev, reg, &temp_lo);
-       mchbar_addr = ((u64)temp_hi << 32) | temp_lo;
-
-       /* If ACPI doesn't have it, assume we need to allocate it ourselves */
-#ifdef CONFIG_PNP
-       if (mchbar_addr &&
-           pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) {
-               ret = 0;
-               goto out;
-       }
-#endif
-
-       /* Get some space for it */
-       ret = pci_bus_alloc_resource(dev_priv->bridge_dev->bus, &dev_priv->mch_res,
-                                    MCHBAR_SIZE, MCHBAR_SIZE,
-                                    PCIBIOS_MIN_MEM,
-                                    0,   pcibios_align_resource,
-                                    dev_priv->bridge_dev);
-       if (ret) {
-               DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret);
-               dev_priv->mch_res.start = 0;
-               goto out;
-       }
-
-       if (IS_I965G(dev))
-               pci_write_config_dword(dev_priv->bridge_dev, reg + 4,
-                                      upper_32_bits(dev_priv->mch_res.start));
-
-       pci_write_config_dword(dev_priv->bridge_dev, reg,
-                              lower_32_bits(dev_priv->mch_res.start));
-out:
-       return ret;
-}
-
-/* Setup MCHBAR if possible, return true if we should disable it again */
-static bool
-intel_setup_mchbar(struct drm_device *dev)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
-       u32 temp;
-       bool need_disable = false, enabled;
-
-       if (IS_I915G(dev) || IS_I915GM(dev)) {
-               pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
-               enabled = !!(temp & DEVEN_MCHBAR_EN);
-       } else {
-               pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
-               enabled = temp & 1;
-       }
-
-       /* If it's already enabled, don't have to do anything */
-       if (enabled)
-               goto out;
-
-       if (intel_alloc_mchbar_resource(dev))
-               goto out;
-
-       need_disable = true;
-
-       /* Space is allocated or reserved, so enable it. */
-       if (IS_I915G(dev) || IS_I915GM(dev)) {
-               pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG,
-                                      temp | DEVEN_MCHBAR_EN);
-       } else {
-               pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
-               pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp | 1);
-       }
-out:
-       return need_disable;
-}
-
-static void
-intel_teardown_mchbar(struct drm_device *dev, bool disable)
-{
-       drm_i915_private_t *dev_priv = dev->dev_private;
-       int mchbar_reg = IS_I965G(dev) ? MCHBAR_I965 : MCHBAR_I915;
-       u32 temp;
-
-       if (disable) {
-               if (IS_I915G(dev) || IS_I915GM(dev)) {
-                       pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
-                       temp &= ~DEVEN_MCHBAR_EN;
-                       pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG, temp);
-               } else {
-                       pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
-                       temp &= ~1;
-                       pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp);
-               }
-       }
-
-       if (dev_priv->mch_res.start)
-               release_resource(&dev_priv->mch_res);
-}
-
 /**
  * Detects bit 6 swizzling of address lookup between IGD access and CPU
  * access through main memory.
@@ -207,9 +91,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
        uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
        uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
-       bool need_disable;
 
-       if (IS_IRONLAKE(dev)) {
+       if (IS_IRONLAKE(dev) || IS_GEN6(dev)) {
                /* On Ironlake whatever DRAM config, GPU always do
                 * same swizzling setup.
                 */
@@ -224,9 +107,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        } else if (IS_MOBILE(dev)) {
                uint32_t dcc;
 
-               /* Try to make sure MCHBAR is enabled before poking at it */
-               need_disable = intel_setup_mchbar(dev);
-
                /* On mobile 9xx chipsets, channel interleave by the CPU is
                 * determined by DCC.  For single-channel, neither the CPU
                 * nor the GPU do swizzling.  For dual channel interleaved,
@@ -266,8 +146,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
                        swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
                        swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
                }
-
-               intel_teardown_mchbar(dev, need_disable);
        } else {
                /* The 965, G33, and newer, have a very flexible memory
                 * configuration.  It will enable dual-channel mode
@@ -302,39 +180,6 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        dev_priv->mm.bit_6_swizzle_y = swizzle_y;
 }
 
-
-/**
- * Returns whether an object is currently fenceable.  If not, it may need
- * to be unbound and have its pitch adjusted.
- */
-bool
-i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj)
-{
-       struct drm_i915_gem_object *obj_priv = obj->driver_private;
-
-       if (IS_I965G(dev)) {
-               /* The 965 can have fences at any page boundary. */
-               if (obj->size & 4095)
-                       return false;
-               return true;
-       } else if (IS_I9XX(dev)) {
-               if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK)
-                       return false;
-       } else {
-               if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK)
-                       return false;
-       }
-
-       /* Power of two sized... */
-       if (obj->size & (obj->size - 1))
-               return false;
-
-       /* Objects must be size aligned as well */
-       if (obj_priv->gtt_offset & (obj->size - 1))
-               return false;
-       return true;
-}
-
 /* Check pitch constriants for all chips & tiling formats */
 bool
 i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
@@ -391,7 +236,7 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
        return true;
 }
 
-static bool
+bool
 i915_gem_object_fence_offset_ok(struct drm_gem_object *obj, int tiling_mode)
 {
        struct drm_device *dev = obj->dev;
@@ -438,9 +283,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
        obj_priv = obj->driver_private;
 
        if (!i915_tiling_ok(dev, args->stride, obj->size, args->tiling_mode)) {
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
+               drm_gem_object_unreference_unlocked(obj);
                return -EINVAL;
        }
 
@@ -493,12 +336,6 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                        goto err;
                }
 
-               /* If we've changed tiling, GTT-mappings of the object
-                * need to re-fault to ensure that the correct fence register
-                * setup is in place.
-                */
-               i915_gem_release_mmap(obj);
-
                obj_priv->tiling_mode = args->tiling_mode;
                obj_priv->stride = args->stride;
        }
index a17d6bdfe63e6efe15d329755049e48ec9ee87cc..5388354da0d176df4ff2a3b7c33de069abff12da 100644 (file)
@@ -166,7 +166,7 @@ void intel_enable_asle (struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                ironlake_enable_display_irq(dev_priv, DE_GSE);
        else
                i915_enable_pipestat(dev_priv, 1,
@@ -269,6 +269,57 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_sysfs_hotplug_event(dev);
 }
 
+static void i915_handle_rps_change(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 busy_up, busy_down, max_avg, min_avg;
+       u16 rgvswctl;
+       u8 new_delay = dev_priv->cur_delay;
+
+       I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS) & ~MEMINT_EVAL_CHG);
+       busy_up = I915_READ(RCPREVBSYTUPAVG);
+       busy_down = I915_READ(RCPREVBSYTDNAVG);
+       max_avg = I915_READ(RCBMAXAVG);
+       min_avg = I915_READ(RCBMINAVG);
+
+       /* Handle RCS change request from hw */
+       if (busy_up > max_avg) {
+               if (dev_priv->cur_delay != dev_priv->max_delay)
+                       new_delay = dev_priv->cur_delay - 1;
+               if (new_delay < dev_priv->max_delay)
+                       new_delay = dev_priv->max_delay;
+       } else if (busy_down < min_avg) {
+               if (dev_priv->cur_delay != dev_priv->min_delay)
+                       new_delay = dev_priv->cur_delay + 1;
+               if (new_delay > dev_priv->min_delay)
+                       new_delay = dev_priv->min_delay;
+       }
+
+       DRM_DEBUG("rps change requested: %d -> %d\n",
+                 dev_priv->cur_delay, new_delay);
+
+       rgvswctl = I915_READ(MEMSWCTL);
+       if (rgvswctl & MEMCTL_CMD_STS) {
+               DRM_ERROR("gpu busy, RCS change rejected\n");
+               return; /* still busy with another command */
+       }
+
+       /* Program the new state */
+       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
+               (new_delay << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       POSTING_READ(MEMSWCTL);
+
+       rgvswctl |= MEMCTL_CMD_STS;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+
+       dev_priv->cur_delay = new_delay;
+
+       DRM_DEBUG("rps changed\n");
+
+       return;
+}
+
 irqreturn_t ironlake_irq_handler(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -331,6 +382,11 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
                queue_work(dev_priv->wq, &dev_priv->hotplug_work);
        }
 
+       if (de_iir & DE_PCU_EVENT) {
+               I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS));
+               i915_handle_rps_change(dev);
+       }
+
        /* should clear PCH hotplug event before clear CPU irq */
        I915_WRITE(SDEIIR, pch_iir);
        I915_WRITE(GTIIR, gt_iir);
@@ -376,6 +432,121 @@ static void i915_error_work_func(struct work_struct *work)
        }
 }
 
+static struct drm_i915_error_object *
+i915_error_object_create(struct drm_device *dev,
+                        struct drm_gem_object *src)
+{
+       struct drm_i915_error_object *dst;
+       struct drm_i915_gem_object *src_priv;
+       int page, page_count;
+
+       if (src == NULL)
+               return NULL;
+
+       src_priv = src->driver_private;
+       if (src_priv->pages == NULL)
+               return NULL;
+
+       page_count = src->size / PAGE_SIZE;
+
+       dst = kmalloc(sizeof(*dst) + page_count * sizeof (u32 *), GFP_ATOMIC);
+       if (dst == NULL)
+               return NULL;
+
+       for (page = 0; page < page_count; page++) {
+               void *s, *d = kmalloc(PAGE_SIZE, GFP_ATOMIC);
+               if (d == NULL)
+                       goto unwind;
+               s = kmap_atomic(src_priv->pages[page], KM_USER0);
+               memcpy(d, s, PAGE_SIZE);
+               kunmap_atomic(s, KM_USER0);
+               dst->pages[page] = d;
+       }
+       dst->page_count = page_count;
+       dst->gtt_offset = src_priv->gtt_offset;
+
+       return dst;
+
+unwind:
+       while (page--)
+               kfree(dst->pages[page]);
+       kfree(dst);
+       return NULL;
+}
+
+static void
+i915_error_object_free(struct drm_i915_error_object *obj)
+{
+       int page;
+
+       if (obj == NULL)
+               return;
+
+       for (page = 0; page < obj->page_count; page++)
+               kfree(obj->pages[page]);
+
+       kfree(obj);
+}
+
+static void
+i915_error_state_free(struct drm_device *dev,
+                     struct drm_i915_error_state *error)
+{
+       i915_error_object_free(error->batchbuffer[0]);
+       i915_error_object_free(error->batchbuffer[1]);
+       i915_error_object_free(error->ringbuffer);
+       kfree(error->active_bo);
+       kfree(error);
+}
+
+static u32
+i915_get_bbaddr(struct drm_device *dev, u32 *ring)
+{
+       u32 cmd;
+
+       if (IS_I830(dev) || IS_845G(dev))
+               cmd = MI_BATCH_BUFFER;
+       else if (IS_I965G(dev))
+               cmd = (MI_BATCH_BUFFER_START | (2 << 6) |
+                      MI_BATCH_NON_SECURE_I965);
+       else
+               cmd = (MI_BATCH_BUFFER_START | (2 << 6));
+
+       return ring[0] == cmd ? ring[1] : 0;
+}
+
+static u32
+i915_ringbuffer_last_batch(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 head, bbaddr;
+       u32 *ring;
+
+       /* Locate the current position in the ringbuffer and walk back
+        * to find the most recently dispatched batch buffer.
+        */
+       bbaddr = 0;
+       head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
+       ring = (u32 *)(dev_priv->ring.virtual_start + head);
+
+       while (--ring >= (u32 *)dev_priv->ring.virtual_start) {
+               bbaddr = i915_get_bbaddr(dev, ring);
+               if (bbaddr)
+                       break;
+       }
+
+       if (bbaddr == 0) {
+               ring = (u32 *)(dev_priv->ring.virtual_start + dev_priv->ring.Size);
+               while (--ring >= (u32 *)dev_priv->ring.virtual_start) {
+                       bbaddr = i915_get_bbaddr(dev, ring);
+                       if (bbaddr)
+                               break;
+               }
+       }
+
+       return bbaddr;
+}
+
 /**
  * i915_capture_error_state - capture an error record for later analysis
  * @dev: drm device
@@ -388,19 +559,26 @@ static void i915_error_work_func(struct work_struct *work)
 static void i915_capture_error_state(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj_priv;
        struct drm_i915_error_state *error;
+       struct drm_gem_object *batchbuffer[2];
        unsigned long flags;
+       u32 bbaddr;
+       int count;
 
        spin_lock_irqsave(&dev_priv->error_lock, flags);
-       if (dev_priv->first_error)
-               goto out;
+       error = dev_priv->first_error;
+       spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+       if (error)
+               return;
 
        error = kmalloc(sizeof(*error), GFP_ATOMIC);
        if (!error) {
-               DRM_DEBUG_DRIVER("out ot memory, not capturing error state\n");
-               goto out;
+               DRM_DEBUG_DRIVER("out of memory, not capturing error state\n");
+               return;
        }
 
+       error->seqno = i915_get_gem_seqno(dev);
        error->eir = I915_READ(EIR);
        error->pgtbl_er = I915_READ(PGTBL_ER);
        error->pipeastat = I915_READ(PIPEASTAT);
@@ -411,6 +589,7 @@ static void i915_capture_error_state(struct drm_device *dev)
                error->ipehr = I915_READ(IPEHR);
                error->instdone = I915_READ(INSTDONE);
                error->acthd = I915_READ(ACTHD);
+               error->bbaddr = 0;
        } else {
                error->ipeir = I915_READ(IPEIR_I965);
                error->ipehr = I915_READ(IPEHR_I965);
@@ -418,14 +597,101 @@ static void i915_capture_error_state(struct drm_device *dev)
                error->instps = I915_READ(INSTPS);
                error->instdone1 = I915_READ(INSTDONE1);
                error->acthd = I915_READ(ACTHD_I965);
+               error->bbaddr = I915_READ64(BB_ADDR);
        }
 
-       do_gettimeofday(&error->time);
+       bbaddr = i915_ringbuffer_last_batch(dev);
+
+       /* Grab the current batchbuffer, most likely to have crashed. */
+       batchbuffer[0] = NULL;
+       batchbuffer[1] = NULL;
+       count = 0;
+       list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list) {
+               struct drm_gem_object *obj = obj_priv->obj;
+
+               if (batchbuffer[0] == NULL &&
+                   bbaddr >= obj_priv->gtt_offset &&
+                   bbaddr < obj_priv->gtt_offset + obj->size)
+                       batchbuffer[0] = obj;
+
+               if (batchbuffer[1] == NULL &&
+                   error->acthd >= obj_priv->gtt_offset &&
+                   error->acthd < obj_priv->gtt_offset + obj->size &&
+                   batchbuffer[0] != obj)
+                       batchbuffer[1] = obj;
+
+               count++;
+       }
 
-       dev_priv->first_error = error;
+       /* We need to copy these to an anonymous buffer as the simplest
+        * method to avoid being overwritten by userpace.
+        */
+       error->batchbuffer[0] = i915_error_object_create(dev, batchbuffer[0]);
+       error->batchbuffer[1] = i915_error_object_create(dev, batchbuffer[1]);
+
+       /* Record the ringbuffer */
+       error->ringbuffer = i915_error_object_create(dev, dev_priv->ring.ring_obj);
+
+       /* Record buffers on the active list. */
+       error->active_bo = NULL;
+       error->active_bo_count = 0;
+
+       if (count)
+               error->active_bo = kmalloc(sizeof(*error->active_bo)*count,
+                                          GFP_ATOMIC);
+
+       if (error->active_bo) {
+               int i = 0;
+               list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list) {
+                       struct drm_gem_object *obj = obj_priv->obj;
+
+                       error->active_bo[i].size = obj->size;
+                       error->active_bo[i].name = obj->name;
+                       error->active_bo[i].seqno = obj_priv->last_rendering_seqno;
+                       error->active_bo[i].gtt_offset = obj_priv->gtt_offset;
+                       error->active_bo[i].read_domains = obj->read_domains;
+                       error->active_bo[i].write_domain = obj->write_domain;
+                       error->active_bo[i].fence_reg = obj_priv->fence_reg;
+                       error->active_bo[i].pinned = 0;
+                       if (obj_priv->pin_count > 0)
+                               error->active_bo[i].pinned = 1;
+                       if (obj_priv->user_pin_count > 0)
+                               error->active_bo[i].pinned = -1;
+                       error->active_bo[i].tiling = obj_priv->tiling_mode;
+                       error->active_bo[i].dirty = obj_priv->dirty;
+                       error->active_bo[i].purgeable = obj_priv->madv != I915_MADV_WILLNEED;
+
+                       if (++i == count)
+                               break;
+               }
+               error->active_bo_count = i;
+       }
+
+       do_gettimeofday(&error->time);
 
-out:
+       spin_lock_irqsave(&dev_priv->error_lock, flags);
+       if (dev_priv->first_error == NULL) {
+               dev_priv->first_error = error;
+               error = NULL;
+       }
        spin_unlock_irqrestore(&dev_priv->error_lock, flags);
+
+       if (error)
+               i915_error_state_free(dev, error);
+}
+
+void i915_destroy_error_state(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_error_state *error;
+
+       spin_lock(&dev_priv->error_lock);
+       error = dev_priv->first_error;
+       dev_priv->first_error = NULL;
+       spin_unlock(&dev_priv->error_lock);
+
+       if (error)
+               i915_error_state_free(dev, error);
 }
 
 /**
@@ -576,7 +842,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 
        atomic_inc(&dev_priv->irq_received);
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return ironlake_irq_handler(dev);
 
        iir = I915_READ(IIR);
@@ -737,7 +1003,7 @@ void i915_user_irq_get(struct drm_device *dev)
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
                else
                        i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
@@ -753,7 +1019,7 @@ void i915_user_irq_put(struct drm_device *dev)
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
        if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
                else
                        i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
@@ -861,7 +1127,7 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
                return -EINVAL;
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                ironlake_enable_display_irq(dev_priv, (pipe == 0) ? 
                                            DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
        else if (IS_I965G(dev))
@@ -883,7 +1149,7 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
        unsigned long irqflags;
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                ironlake_disable_display_irq(dev_priv, (pipe == 0) ? 
                                             DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
        else
@@ -897,7 +1163,7 @@ void i915_enable_interrupt (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (!IS_IRONLAKE(dev))
+       if (!HAS_PCH_SPLIT(dev))
                opregion_enable_asle(dev);
        dev_priv->irq_enabled = 1;
 }
@@ -973,7 +1239,11 @@ void i915_hangcheck_elapsed(unsigned long data)
        struct drm_device *dev = (struct drm_device *)data;
        drm_i915_private_t *dev_priv = dev->dev_private;
        uint32_t acthd;
-       
+
+       /* No reset support on this chip yet. */
+       if (IS_GEN6(dev))
+               return;
+
        if (!IS_I965G(dev))
                acthd = I915_READ(ACTHD);
        else
@@ -1064,6 +1334,13 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg);
        (void) I915_READ(SDEIER);
 
+       if (IS_IRONLAKE_M(dev)) {
+               /* Clear & enable PCU event interrupts */
+               I915_WRITE(DEIIR, DE_PCU_EVENT);
+               I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT);
+               ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT);
+       }
+
        return 0;
 }
 
@@ -1076,7 +1353,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
        INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
        INIT_WORK(&dev_priv->error_work, i915_error_work_func);
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                ironlake_irq_preinstall(dev);
                return;
        }
@@ -1108,7 +1385,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
 
        dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return ironlake_irq_postinstall(dev);
 
        /* Unmask the interrupts that we always want on. */
@@ -1196,7 +1473,7 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
 
        dev_priv->vblank_pipe = 0;
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                ironlake_irq_uninstall(dev);
                return;
        }
index ab1bd2d3d3b64a20d2b4362d3f418e6e9eb1e140..3d59862c7ccd01d9c14904c97ef1b4853c9a22b8 100644 (file)
 #define INTEL_GMCH_GMS_STOLEN_224M     (0xc << 4)
 #define INTEL_GMCH_GMS_STOLEN_352M     (0xd << 4)
 
+#define SNB_GMCH_CTRL  0x50
+#define SNB_GMCH_GMS_STOLEN_MASK       0xF8
+#define SNB_GMCH_GMS_STOLEN_32M                (1 << 3)
+#define SNB_GMCH_GMS_STOLEN_64M                (2 << 3)
+#define SNB_GMCH_GMS_STOLEN_96M                (3 << 3)
+#define SNB_GMCH_GMS_STOLEN_128M       (4 << 3)
+#define SNB_GMCH_GMS_STOLEN_160M       (5 << 3)
+#define SNB_GMCH_GMS_STOLEN_192M       (6 << 3)
+#define SNB_GMCH_GMS_STOLEN_224M       (7 << 3)
+#define SNB_GMCH_GMS_STOLEN_256M       (8 << 3)
+#define SNB_GMCH_GMS_STOLEN_288M       (9 << 3)
+#define SNB_GMCH_GMS_STOLEN_320M       (0xa << 3)
+#define SNB_GMCH_GMS_STOLEN_352M       (0xb << 3)
+#define SNB_GMCH_GMS_STOLEN_384M       (0xc << 3)
+#define SNB_GMCH_GMS_STOLEN_416M       (0xd << 3)
+#define SNB_GMCH_GMS_STOLEN_448M       (0xe << 3)
+#define SNB_GMCH_GMS_STOLEN_480M       (0xf << 3)
+#define SNB_GMCH_GMS_STOLEN_512M       (0x10 << 3)
+
 /* PCI config space */
 
 #define HPLLCC 0xc0 /* 855 only */
@@ -61,6 +80,7 @@
 #define   GC_CLOCK_100_200             (1 << 0)
 #define   GC_CLOCK_100_133             (2 << 0)
 #define   GC_CLOCK_166_250             (3 << 0)
+#define GCFGC2 0xda
 #define GCFGC  0xf0 /* 915+ only */
 #define   GC_LOW_FREQUENCY_ENABLE      (1 << 7)
 #define   GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4)
 #define   I965_FENCE_REG_VALID         (1<<0)
 #define   I965_FENCE_MAX_PITCH_VAL     0x0400
 
+#define FENCE_REG_SANDYBRIDGE_0                0x100000
+#define   SANDYBRIDGE_FENCE_PITCH_SHIFT        32
+
 /*
  * Instruction and interrupt control regs
  */
 #define INSTDONE1      0x0207c /* 965+ only */
 #define ACTHD_I965     0x02074
 #define HWS_PGA                0x02080
+#define HWS_PGA_GEN6   0x04080
 #define HWS_ADDRESS_MASK       0xfffff000
 #define HWS_START_ADDRESS_SHIFT        4
 #define PWRCTXA                0x2088 /* 965GM+ only */
 #define   I915_PIPE_CONTROL_NOTIFY_INTERRUPT           (1<<18)
 #define   I915_DISPLAY_PORT_INTERRUPT                  (1<<17)
 #define   I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT   (1<<15)
-#define   I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT     (1<<14)
+#define   I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT     (1<<14) /* p-state */
 #define   I915_HWB_OOM_INTERRUPT                       (1<<13)
 #define   I915_SYNC_STATUS_INTERRUPT                   (1<<12)
 #define   I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT  (1<<11)
 #define   I915_ERROR_MEMORY_REFRESH                    (1<<1)
 #define   I915_ERROR_INSTRUCTION                       (1<<0)
 #define INSTPM         0x020c0
+#define   INSTPM_SELF_EN (1<<12) /* 915GM only */
 #define ACTHD          0x020c8
 #define FW_BLC         0x020d8
 #define FW_BLC2                0x020dc
 #define FW_BLC_SELF    0x020e0 /* 915+ only */
-#define   FW_BLC_SELF_EN (1<<15)
+#define   FW_BLC_SELF_EN_MASK      (1<<31)
+#define   FW_BLC_SELF_FIFO_MASK    (1<<16) /* 945 only */
+#define   FW_BLC_SELF_EN           (1<<15) /* 945 only */
 #define MM_BURST_LENGTH     0x00700000
 #define MM_FIFO_WATERMARK   0x0001F000
 #define LM_BURST_LENGTH     0x00000700
 #define   CM0_COLOR_EVICT_DISABLE (1<<3)
 #define   CM0_DEPTH_WRITE_DISABLE (1<<1)
 #define   CM0_RC_OP_FLUSH_DISABLE (1<<0)
+#define BB_ADDR                0x02140 /* 8 bytes */
 #define GFX_FLSH_CNTL  0x02170 /* 915+ only */
 
 
 #define CLKCFG_MEM_800                                 (3 << 4)
 #define CLKCFG_MEM_MASK                                        (7 << 4)
 
-/** GM965 GM45 render standby register */
-#define MCHBAR_RENDER_STANDBY  0x111B8
+#define CRSTANDVID             0x11100
+#define PXVFREQ_BASE           0x11110 /* P[0-15]VIDFREQ (0x1114c) (Ironlake) */
+#define   PXVFREQ_PX_MASK      0x7f000000
+#define   PXVFREQ_PX_SHIFT     24
+#define VIDFREQ_BASE           0x11110
+#define VIDFREQ1               0x11110 /* VIDFREQ1-4 (0x1111c) (Cantiga) */
+#define VIDFREQ2               0x11114
+#define VIDFREQ3               0x11118
+#define VIDFREQ4               0x1111c
+#define   VIDFREQ_P0_MASK      0x1f000000
+#define   VIDFREQ_P0_SHIFT     24
+#define   VIDFREQ_P0_CSCLK_MASK        0x00f00000
+#define   VIDFREQ_P0_CSCLK_SHIFT 20
+#define   VIDFREQ_P0_CRCLK_MASK        0x000f0000
+#define   VIDFREQ_P0_CRCLK_SHIFT 16
+#define   VIDFREQ_P1_MASK      0x00001f00
+#define   VIDFREQ_P1_SHIFT     8
+#define   VIDFREQ_P1_CSCLK_MASK        0x000000f0
+#define   VIDFREQ_P1_CSCLK_SHIFT 4
+#define   VIDFREQ_P1_CRCLK_MASK        0x0000000f
+#define INTTOEXT_BASE_ILK      0x11300
+#define INTTOEXT_BASE          0x11120 /* INTTOEXT1-8 (0x1113c) */
+#define   INTTOEXT_MAP3_SHIFT  24
+#define   INTTOEXT_MAP3_MASK   (0x1f << INTTOEXT_MAP3_SHIFT)
+#define   INTTOEXT_MAP2_SHIFT  16
+#define   INTTOEXT_MAP2_MASK   (0x1f << INTTOEXT_MAP2_SHIFT)
+#define   INTTOEXT_MAP1_SHIFT  8
+#define   INTTOEXT_MAP1_MASK   (0x1f << INTTOEXT_MAP1_SHIFT)
+#define   INTTOEXT_MAP0_SHIFT  0
+#define   INTTOEXT_MAP0_MASK   (0x1f << INTTOEXT_MAP0_SHIFT)
+#define MEMSWCTL               0x11170 /* Ironlake only */
+#define   MEMCTL_CMD_MASK      0xe000
+#define   MEMCTL_CMD_SHIFT     13
+#define   MEMCTL_CMD_RCLK_OFF  0
+#define   MEMCTL_CMD_RCLK_ON   1
+#define   MEMCTL_CMD_CHFREQ    2
+#define   MEMCTL_CMD_CHVID     3
+#define   MEMCTL_CMD_VMMOFF    4
+#define   MEMCTL_CMD_VMMON     5
+#define   MEMCTL_CMD_STS       (1<<12) /* write 1 triggers command, clears
+                                          when command complete */
+#define   MEMCTL_FREQ_MASK     0x0f00 /* jitter, from 0-15 */
+#define   MEMCTL_FREQ_SHIFT    8
+#define   MEMCTL_SFCAVM                (1<<7)
+#define   MEMCTL_TGT_VID_MASK  0x007f
+#define MEMIHYST               0x1117c
+#define MEMINTREN              0x11180 /* 16 bits */
+#define   MEMINT_RSEXIT_EN     (1<<8)
+#define   MEMINT_CX_SUPR_EN    (1<<7)
+#define   MEMINT_CONT_BUSY_EN  (1<<6)
+#define   MEMINT_AVG_BUSY_EN   (1<<5)
+#define   MEMINT_EVAL_CHG_EN   (1<<4)
+#define   MEMINT_MON_IDLE_EN   (1<<3)
+#define   MEMINT_UP_EVAL_EN    (1<<2)
+#define   MEMINT_DOWN_EVAL_EN  (1<<1)
+#define   MEMINT_SW_CMD_EN     (1<<0)
+#define MEMINTRSTR             0x11182 /* 16 bits */
+#define   MEM_RSEXIT_MASK      0xc000
+#define   MEM_RSEXIT_SHIFT     14
+#define   MEM_CONT_BUSY_MASK   0x3000
+#define   MEM_CONT_BUSY_SHIFT  12
+#define   MEM_AVG_BUSY_MASK    0x0c00
+#define   MEM_AVG_BUSY_SHIFT   10
+#define   MEM_EVAL_CHG_MASK    0x0300
+#define   MEM_EVAL_BUSY_SHIFT  8
+#define   MEM_MON_IDLE_MASK    0x00c0
+#define   MEM_MON_IDLE_SHIFT   6
+#define   MEM_UP_EVAL_MASK     0x0030
+#define   MEM_UP_EVAL_SHIFT    4
+#define   MEM_DOWN_EVAL_MASK   0x000c
+#define   MEM_DOWN_EVAL_SHIFT  2
+#define   MEM_SW_CMD_MASK      0x0003
+#define   MEM_INT_STEER_GFX    0
+#define   MEM_INT_STEER_CMR    1
+#define   MEM_INT_STEER_SMI    2
+#define   MEM_INT_STEER_SCI    3
+#define MEMINTRSTS             0x11184
+#define   MEMINT_RSEXIT                (1<<7)
+#define   MEMINT_CONT_BUSY     (1<<6)
+#define   MEMINT_AVG_BUSY      (1<<5)
+#define   MEMINT_EVAL_CHG      (1<<4)
+#define   MEMINT_MON_IDLE      (1<<3)
+#define   MEMINT_UP_EVAL       (1<<2)
+#define   MEMINT_DOWN_EVAL     (1<<1)
+#define   MEMINT_SW_CMD                (1<<0)
+#define MEMMODECTL             0x11190
+#define   MEMMODE_BOOST_EN     (1<<31)
+#define   MEMMODE_BOOST_FREQ_MASK 0x0f000000 /* jitter for boost, 0-15 */
+#define   MEMMODE_BOOST_FREQ_SHIFT 24
+#define   MEMMODE_IDLE_MODE_MASK 0x00030000
+#define   MEMMODE_IDLE_MODE_SHIFT 16
+#define   MEMMODE_IDLE_MODE_EVAL 0
+#define   MEMMODE_IDLE_MODE_CONT 1
+#define   MEMMODE_HWIDLE_EN    (1<<15)
+#define   MEMMODE_SWMODE_EN    (1<<14)
+#define   MEMMODE_RCLK_GATE    (1<<13)
+#define   MEMMODE_HW_UPDATE    (1<<12)
+#define   MEMMODE_FSTART_MASK  0x00000f00 /* starting jitter, 0-15 */
+#define   MEMMODE_FSTART_SHIFT 8
+#define   MEMMODE_FMAX_MASK    0x000000f0 /* max jitter, 0-15 */
+#define   MEMMODE_FMAX_SHIFT   4
+#define   MEMMODE_FMIN_MASK    0x0000000f /* min jitter, 0-15 */
+#define RCBMAXAVG              0x1119c
+#define MEMSWCTL2              0x1119e /* Cantiga only */
+#define   SWMEMCMD_RENDER_OFF  (0 << 13)
+#define   SWMEMCMD_RENDER_ON   (1 << 13)
+#define   SWMEMCMD_SWFREQ      (2 << 13)
+#define   SWMEMCMD_TARVID      (3 << 13)
+#define   SWMEMCMD_VRM_OFF     (4 << 13)
+#define   SWMEMCMD_VRM_ON      (5 << 13)
+#define   CMDSTS               (1<<12)
+#define   SFCAVM               (1<<11)
+#define   SWFREQ_MASK          0x0380 /* P0-7 */
+#define   SWFREQ_SHIFT         7
+#define   TARVID_MASK          0x001f
+#define MEMSTAT_CTG            0x111a0
+#define RCBMINAVG              0x111a0
+#define RCUPEI                 0x111b0
+#define RCDNEI                 0x111b4
+#define MCHBAR_RENDER_STANDBY          0x111b8
 #define   RCX_SW_EXIT          (1<<23)
 #define   RSX_STATUS_MASK      0x00700000
+#define VIDCTL                 0x111c0
+#define VIDSTS                 0x111c8
+#define VIDSTART               0x111cc /* 8 bits */
+#define MEMSTAT_ILK                    0x111f8
+#define   MEMSTAT_VID_MASK     0x7f00
+#define   MEMSTAT_VID_SHIFT    8
+#define   MEMSTAT_PSTATE_MASK  0x00f8
+#define   MEMSTAT_PSTATE_SHIFT  3
+#define   MEMSTAT_MON_ACTV     (1<<2)
+#define   MEMSTAT_SRC_CTL_MASK 0x0003
+#define   MEMSTAT_SRC_CTL_CORE 0
+#define   MEMSTAT_SRC_CTL_TRB  1
+#define   MEMSTAT_SRC_CTL_THM  2
+#define   MEMSTAT_SRC_CTL_STDBY 3
+#define RCPREVBSYTUPAVG                0x113b8
+#define RCPREVBSYTDNAVG                0x113bc
 #define PEG_BAND_GAP_DATA      0x14d68
 
 /*
index a3b90c9561dc6623b6671ad4aaefbfd957e65ed8..ac0d1a73ac224174f603a0bc3429e6a97464a2b6 100644 (file)
@@ -682,6 +682,8 @@ void i915_restore_display(struct drm_device *dev)
                I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS);
                I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR);
                I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL);
+               I915_WRITE(MCHBAR_RENDER_STANDBY,
+                          dev_priv->saveMCHBAR_RENDER_STANDBY);
        } else {
                I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS);
                I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
@@ -745,11 +747,16 @@ int i915_save_state(struct drm_device *dev)
                dev_priv->saveGTIMR = I915_READ(GTIMR);
                dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR);
                dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR);
+               dev_priv->saveMCHBAR_RENDER_STANDBY =
+                       I915_READ(MCHBAR_RENDER_STANDBY);
        } else {
                dev_priv->saveIER = I915_READ(IER);
                dev_priv->saveIMR = I915_READ(IMR);
        }
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_disable_drps(dev);
+
        /* Cache mode state */
        dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
 
@@ -820,6 +827,9 @@ int i915_restore_state(struct drm_device *dev)
        /* Clock gating state */
        intel_init_clock_gating(dev);
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_enable_drps(dev);
+
        /* Cache mode state */
        I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
 
index 15fbc1b5a83ebe0d3df0afd331b558531a0ac188..70c9d4ba7042b57dc677a9e2fcbcf03a6abe5e4e 100644 (file)
@@ -247,6 +247,7 @@ static void
 parse_general_features(struct drm_i915_private *dev_priv,
                       struct bdb_header *bdb)
 {
+       struct drm_device *dev = dev_priv->dev;
        struct bdb_general_features *general;
 
        /* Set sensible defaults in case we can't find the general block */
@@ -263,7 +264,7 @@ parse_general_features(struct drm_i915_private *dev_priv,
                        if (IS_I85X(dev_priv->dev))
                                dev_priv->lvds_ssc_freq =
                                        general->ssc_freq ? 66 : 48;
-                       else if (IS_IRONLAKE(dev_priv->dev))
+                       else if (IS_IRONLAKE(dev_priv->dev) || IS_GEN6(dev))
                                dev_priv->lvds_ssc_freq =
                                        general->ssc_freq ? 100 : 120;
                        else
index 79dd4026586fa55f6072b05bb27a9fce2734099a..fccf07470c8f6f512a8950555095ca122ed95c08 100644 (file)
@@ -39,7 +39,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 temp, reg;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                reg = PCH_ADPA;
        else
                reg = ADPA;
@@ -113,7 +113,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
        else
                dpll_md_reg = DPLL_B_MD;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                adpa_reg = PCH_ADPA;
        else
                adpa_reg = ADPA;
@@ -122,7 +122,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
         * Disable separate mode multiplier used when cloning SDVO to CRT
         * XXX this needs to be adjusted when we really are cloning
         */
-       if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
+       if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
                dpll_md = I915_READ(dpll_md_reg);
                I915_WRITE(dpll_md_reg,
                           dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
@@ -136,11 +136,11 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 
        if (intel_crtc->pipe == 0) {
                adpa |= ADPA_PIPE_A_SELECT;
-               if (!IS_IRONLAKE(dev))
+               if (!HAS_PCH_SPLIT(dev))
                        I915_WRITE(BCLRPAT_A, 0);
        } else {
                adpa |= ADPA_PIPE_B_SELECT;
-               if (!IS_IRONLAKE(dev))
+               if (!HAS_PCH_SPLIT(dev))
                        I915_WRITE(BCLRPAT_B, 0);
        }
 
@@ -202,7 +202,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
        u32 hotplug_en;
        int i, tries = 0;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return intel_ironlake_crt_detect_hotplug(connector);
 
        /*
@@ -524,7 +524,7 @@ void intel_crt_init(struct drm_device *dev)
                                          &intel_output->enc);
 
        /* Set up the DDC bus. */
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                i2c_reg = PCH_GPIOA;
        else {
                i2c_reg = GPIOA;
index b27202d23ebc1d9aab4a327eec55d733bd43cae8..9cd6de5f99061a2916af6d453dd46e2de2ea11fe 100644 (file)
@@ -232,7 +232,7 @@ struct intel_limit {
 #define G4X_P2_DISPLAY_PORT_FAST           10
 #define G4X_P2_DISPLAY_PORT_LIMIT          0
 
-/* Ironlake */
+/* Ironlake / Sandybridge */
 /* as we calculate clock using (register_value + 2) for
    N/M1/M2, so here the range value for them is (actual_value-2).
  */
@@ -690,7 +690,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        const intel_limit_t *limit;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                limit = intel_ironlake_limit(crtc);
        else if (IS_G4X(dev)) {
                limit = intel_g4x_limit(crtc);
@@ -886,7 +886,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
                int lvds_reg;
 
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        lvds_reg = PCH_LVDS;
                else
                        lvds_reg = LVDS;
@@ -1188,25 +1188,30 @@ static void intel_update_fbc(struct drm_crtc *crtc,
        if (intel_fb->obj->size > dev_priv->cfb_size) {
                DRM_DEBUG_KMS("framebuffer too large, disabling "
                                "compression\n");
+               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
                goto out_disable;
        }
        if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
            (mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
                DRM_DEBUG_KMS("mode incompatible with compression, "
                                "disabling\n");
+               dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE;
                goto out_disable;
        }
        if ((mode->hdisplay > 2048) ||
            (mode->vdisplay > 1536)) {
                DRM_DEBUG_KMS("mode too large for compression, disabling\n");
+               dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE;
                goto out_disable;
        }
        if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) {
                DRM_DEBUG_KMS("plane not 0, disabling compression\n");
+               dev_priv->no_fbc_reason = FBC_BAD_PLANE;
                goto out_disable;
        }
        if (obj_priv->tiling_mode != I915_TILING_X) {
                DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
+               dev_priv->no_fbc_reason = FBC_NOT_TILED;
                goto out_disable;
        }
 
@@ -1366,7 +1371,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                        dspcntr &= ~DISPPLANE_TILED;
        }
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                /* must disable */
                dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
@@ -1427,7 +1432,7 @@ static void i915_disable_vga (struct drm_device *dev)
        u8 sr1;
        u32 vga_reg;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                vga_reg = CPU_VGACNTRL;
        else
                vga_reg = VGACNTRL;
@@ -2111,7 +2116,7 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
                                  struct drm_display_mode *adjusted_mode)
 {
        struct drm_device *dev = crtc->dev;
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                /* FDI link clock is fixed at 2.7G */
                if (mode->clock * 3 > 27000 * 4)
                        return MODE_CLOCK_HIGH;
@@ -2757,11 +2762,22 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
                srwm = total_size - sr_entries;
                if (srwm < 0)
                        srwm = 1;
-               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
+
+               if (IS_I945G(dev) || IS_I945GM(dev))
+                       I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_FIFO_MASK | (srwm & 0xff));
+               else if (IS_I915GM(dev)) {
+                       /* 915M has a smaller SRWM field */
+                       I915_WRITE(FW_BLC_SELF, srwm & 0x3f);
+                       I915_WRITE(INSTPM, I915_READ(INSTPM) | INSTPM_SELF_EN);
+               }
        } else {
                /* Turn off self refresh if both pipes are enabled */
-               I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
-                                       & ~FW_BLC_SELF_EN);
+               if (IS_I945G(dev) || IS_I945GM(dev)) {
+                       I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
+                                  & ~FW_BLC_SELF_EN);
+               } else if (IS_I915GM(dev)) {
+                       I915_WRITE(INSTPM, I915_READ(INSTPM) & ~INSTPM_SELF_EN);
+               }
        }
 
        DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
@@ -2967,7 +2983,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                                        refclk / 1000);
        } else if (IS_I9XX(dev)) {
                refclk = 96000;
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        refclk = 120000; /* 120Mhz refclk */
        } else {
                refclk = 48000;
@@ -3025,7 +3041,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        /* FDI link */
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                int lane, link_bw, bpp;
                /* eDP doesn't require FDI link, so just set DP M/N
                   according to current link config */
@@ -3102,7 +3118,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
         * PCH B stepping, previous chipset stepping should be
         * ignoring this setting.
         */
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                temp = I915_READ(PCH_DREF_CONTROL);
                /* Always enable nonspread source */
                temp &= ~DREF_NONSPREAD_SOURCE_MASK;
@@ -3149,7 +3165,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                                reduced_clock.m2;
        }
 
-       if (!IS_IRONLAKE(dev))
+       if (!HAS_PCH_SPLIT(dev))
                dpll = DPLL_VGA_MODE_DIS;
 
        if (IS_I9XX(dev)) {
@@ -3162,7 +3178,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
                        if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-                       else if (IS_IRONLAKE(dev))
+                       else if (HAS_PCH_SPLIT(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
                }
                if (is_dp)
@@ -3174,7 +3190,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                else {
                        dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
                        /* also FPA1 */
-                       if (IS_IRONLAKE(dev))
+                       if (HAS_PCH_SPLIT(dev))
                                dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
                        if (IS_G4X(dev) && has_reduced_clock)
                                dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
@@ -3193,7 +3209,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
                        break;
                }
-               if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+               if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev))
                        dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
        } else {
                if (is_lvds) {
@@ -3227,7 +3243,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        /* Ironlake's plane is forced to pipe, bit 24 is to
           enable color space conversion */
-       if (!IS_IRONLAKE(dev)) {
+       if (!HAS_PCH_SPLIT(dev)) {
                if (pipe == 0)
                        dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
                else
@@ -3254,14 +3270,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
 
        /* Disable the panel fitter if it was on our pipe */
-       if (!IS_IRONLAKE(dev) && intel_panel_fitter_pipe(dev) == pipe)
+       if (!HAS_PCH_SPLIT(dev) && intel_panel_fitter_pipe(dev) == pipe)
                I915_WRITE(PFIT_CONTROL, 0);
 
        DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
        drm_mode_debug_printmodeline(mode);
 
        /* assign to Ironlake registers */
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                fp_reg = pch_fp_reg;
                dpll_reg = pch_dpll_reg;
        }
@@ -3282,7 +3298,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        if (is_lvds) {
                u32 lvds;
 
-               if (IS_IRONLAKE(dev))
+               if (HAS_PCH_SPLIT(dev))
                        lvds_reg = PCH_LVDS;
 
                lvds = I915_READ(lvds_reg);
@@ -3304,12 +3320,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                /* set the dithering flag */
                if (IS_I965G(dev)) {
                        if (dev_priv->lvds_dither) {
-                               if (IS_IRONLAKE(dev))
+                               if (HAS_PCH_SPLIT(dev))
                                        pipeconf |= PIPE_ENABLE_DITHER;
                                else
                                        lvds |= LVDS_ENABLE_DITHER;
                        } else {
-                               if (IS_IRONLAKE(dev))
+                               if (HAS_PCH_SPLIT(dev))
                                        pipeconf &= ~PIPE_ENABLE_DITHER;
                                else
                                        lvds &= ~LVDS_ENABLE_DITHER;
@@ -3328,7 +3344,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                /* Wait for the clocks to stabilize. */
                udelay(150);
 
-               if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
+               if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
                        if (is_sdvo) {
                                sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
                                I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
@@ -3375,14 +3391,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        /* pipesrc and dspsize control the size that is scaled from, which should
         * always be the user's requested size.
         */
-       if (!IS_IRONLAKE(dev)) {
+       if (!HAS_PCH_SPLIT(dev)) {
                I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
                                (mode->hdisplay - 1));
                I915_WRITE(dsppos_reg, 0);
        }
        I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
                I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
                I915_WRITE(link_m1_reg, m_n.link_m);
@@ -3438,7 +3454,7 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
                return;
 
        /* use legacy palette for Ironlake */
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
                                                   LGC_PALETTE_B;
 
@@ -3553,11 +3569,10 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        intel_crtc->cursor_bo = bo;
 
        return 0;
-fail:
-       mutex_lock(&dev->struct_mutex);
 fail_locked:
-       drm_gem_object_unreference(bo);
        mutex_unlock(&dev->struct_mutex);
+fail:
+       drm_gem_object_unreference_unlocked(bo);
        return ret;
 }
 
@@ -3922,7 +3937,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
        int dpll = I915_READ(dpll_reg);
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return;
 
        if (!dev_priv->lvds_downclock_avail)
@@ -3961,7 +3976,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
        int dpll = I915_READ(dpll_reg);
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return;
 
        if (!dev_priv->lvds_downclock_avail)
@@ -4011,6 +4026,11 @@ static void intel_idle_update(struct work_struct *work)
 
        mutex_lock(&dev->struct_mutex);
 
+       if (IS_I945G(dev) || IS_I945GM(dev)) {
+               DRM_DEBUG_DRIVER("enable memory self refresh on 945\n");
+               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN);
+       }
+
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                /* Skip inactive CRTCs */
                if (!crtc->fb)
@@ -4044,9 +4064,17 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       if (!dev_priv->busy)
+       if (!dev_priv->busy) {
+               if (IS_I945G(dev) || IS_I945GM(dev)) {
+                       u32 fw_blc_self;
+
+                       DRM_DEBUG_DRIVER("disable memory self refresh on 945\n");
+                       fw_blc_self = I915_READ(FW_BLC_SELF);
+                       fw_blc_self &= ~FW_BLC_SELF_EN;
+                       I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK);
+               }
                dev_priv->busy = true;
-       else
+       else
                mod_timer(&dev_priv->idle_timer, jiffies +
                          msecs_to_jiffies(GPU_IDLE_TIMEOUT));
 
@@ -4058,6 +4086,14 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
                intel_fb = to_intel_framebuffer(crtc->fb);
                if (intel_fb->obj == obj) {
                        if (!intel_crtc->busy) {
+                               if (IS_I945G(dev) || IS_I945GM(dev)) {
+                                       u32 fw_blc_self;
+
+                                       DRM_DEBUG_DRIVER("disable memory self refresh on 945\n");
+                                       fw_blc_self = I915_READ(FW_BLC_SELF);
+                                       fw_blc_self &= ~FW_BLC_SELF_EN;
+                                       I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK);
+                               }
                                /* Non-busy -> busy, upclock */
                                intel_increase_pllclock(crtc, true);
                                intel_crtc->busy = true;
@@ -4382,7 +4418,7 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (IS_MOBILE(dev) && !IS_I830(dev))
                intel_lvds_init(dev);
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                int found;
 
                if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
@@ -4451,7 +4487,7 @@ static void intel_setup_outputs(struct drm_device *dev)
                        DRM_DEBUG_KMS("probing DP_D\n");
                        intel_dp_init(dev, DP_D);
                }
-       } else if (IS_I8XX(dev))
+       } else if (IS_GEN2(dev))
                intel_dvo_init(dev);
 
        if (SUPPORTS_TV(dev))
@@ -4476,9 +4512,7 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
                intelfb_remove(dev, fb);
 
        drm_framebuffer_cleanup(fb);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(intel_fb->obj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(intel_fb->obj);
 
        kfree(intel_fb);
 }
@@ -4541,9 +4575,7 @@ intel_user_framebuffer_create(struct drm_device *dev,
 
        ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj);
        if (ret) {
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(obj);
-               mutex_unlock(&dev->struct_mutex);
+               drm_gem_object_unreference_unlocked(obj);
                return NULL;
        }
 
@@ -4591,6 +4623,91 @@ err_unref:
        return NULL;
 }
 
+void ironlake_enable_drps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 rgvmodectl = I915_READ(MEMMODECTL), rgvswctl;
+       u8 fmax, fmin, fstart, vstart;
+       int i = 0;
+
+       /* 100ms RC evaluation intervals */
+       I915_WRITE(RCUPEI, 100000);
+       I915_WRITE(RCDNEI, 100000);
+
+       /* Set max/min thresholds to 90ms and 80ms respectively */
+       I915_WRITE(RCBMAXAVG, 90000);
+       I915_WRITE(RCBMINAVG, 80000);
+
+       I915_WRITE(MEMIHYST, 1);
+
+       /* Set up min, max, and cur for interrupt handling */
+       fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT;
+       fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
+       fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
+               MEMMODE_FSTART_SHIFT;
+       vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
+               PXVFREQ_PX_SHIFT;
+
+       dev_priv->max_delay = fstart; /* can't go to fmax w/o IPS */
+       dev_priv->min_delay = fmin;
+       dev_priv->cur_delay = fstart;
+
+       I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
+
+       /*
+        * Interrupts will be enabled in ironlake_irq_postinstall
+        */
+
+       I915_WRITE(VIDSTART, vstart);
+       POSTING_READ(VIDSTART);
+
+       rgvmodectl |= MEMMODE_SWMODE_EN;
+       I915_WRITE(MEMMODECTL, rgvmodectl);
+
+       while (I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) {
+               if (i++ > 100) {
+                       DRM_ERROR("stuck trying to change perf mode\n");
+                       break;
+               }
+               msleep(1);
+       }
+       msleep(1);
+
+       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
+               (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       POSTING_READ(MEMSWCTL);
+
+       rgvswctl |= MEMCTL_CMD_STS;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+}
+
+void ironlake_disable_drps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 rgvswctl;
+       u8 fstart;
+
+       /* Ack interrupts, disable EFC interrupt */
+       I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
+       I915_WRITE(MEMINTRSTS, MEMINT_EVAL_CHG);
+       I915_WRITE(DEIER, I915_READ(DEIER) & ~DE_PCU_EVENT);
+       I915_WRITE(DEIIR, DE_PCU_EVENT);
+       I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
+
+       /* Go back to the starting frequency */
+       fstart = (I915_READ(MEMMODECTL) & MEMMODE_FSTART_MASK) >>
+               MEMMODE_FSTART_SHIFT;
+       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
+               (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       msleep(1);
+       rgvswctl |= MEMCTL_CMD_STS;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       msleep(1);
+
+}
+
 void intel_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4599,7 +4716,7 @@ void intel_init_clock_gating(struct drm_device *dev)
         * Disable clock gating reported to work incorrectly according to the
         * specs, but enable as much else as we can.
         */
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                return;
        } else if (IS_G4X(dev)) {
                uint32_t dspclk_gate;
@@ -4672,7 +4789,7 @@ static void intel_init_display(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* We always want a DPMS function */
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                dev_priv->display.dpms = ironlake_crtc_dpms;
        else
                dev_priv->display.dpms = i9xx_crtc_dpms;
@@ -4715,7 +4832,7 @@ static void intel_init_display(struct drm_device *dev)
                        i830_get_display_clock_speed;
 
        /* For FIFO watermark updates */
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                dev_priv->display.update_wm = NULL;
        else if (IS_G4X(dev))
                dev_priv->display.update_wm = g4x_update_wm;
@@ -4774,11 +4891,6 @@ void intel_modeset_init(struct drm_device *dev)
        DRM_DEBUG_KMS("%d display pipe%s available.\n",
                  num_pipe, num_pipe > 1 ? "s" : "");
 
-       if (IS_I85X(dev))
-               pci_read_config_word(dev->pdev, HPLLCC, &dev_priv->orig_clock);
-       else if (IS_I9XX(dev) || IS_G4X(dev))
-               pci_read_config_word(dev->pdev, GCFGC, &dev_priv->orig_clock);
-
        for (i = 0; i < num_pipe; i++) {
                intel_crtc_init(dev, i);
        }
@@ -4787,6 +4899,9 @@ void intel_modeset_init(struct drm_device *dev)
 
        intel_init_clock_gating(dev);
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_enable_drps(dev);
+
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
                    (unsigned long)dev);
@@ -4834,6 +4949,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
                drm_gem_object_unreference(dev_priv->pwrctx);
        }
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_disable_drps(dev);
+
        mutex_unlock(&dev->struct_mutex);
 
        drm_mode_config_cleanup(dev);
index 439506cefc146c92afdece74e8c8a89476904c68..3ef3a0d0edd0a45a8ac50f9c420dca4695248bd5 100644 (file)
@@ -231,7 +231,7 @@ intel_dp_aux_ch(struct intel_output *intel_output,
         */
        if (IS_eDP(intel_output))
                aux_clock_divider = 225; /* eDP input clock at 450Mhz */
-       else if (IS_IRONLAKE(dev))
+       else if (HAS_PCH_SPLIT(dev))
                aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */
        else
                aux_clock_divider = intel_hrawclk(dev) / 2;
@@ -584,7 +584,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
        intel_dp_compute_m_n(3, lane_count,
                             mode->clock, adjusted_mode->clock, &m_n);
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                if (intel_crtc->pipe == 0) {
                        I915_WRITE(TRANSA_DATA_M1,
                                   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
@@ -1176,7 +1176,7 @@ intel_dp_detect(struct drm_connector *connector)
 
        dp_priv->has_audio = false;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return ironlake_dp_detect(connector);
 
        temp = I915_READ(PORT_HOTPLUG_EN);
index a51573da1ff601f5cd868adf7485249b3708d45a..3a467ca578579a09f6af9731608611bcacd904e0 100644 (file)
@@ -209,6 +209,8 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
 extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
                                    u16 *blue, int regno);
 extern void intel_init_clock_gating(struct drm_device *dev);
+extern void ironlake_enable_drps(struct drm_device *dev);
+extern void ironlake_disable_drps(struct drm_device *dev);
 
 extern int intel_framebuffer_create(struct drm_device *dev,
                                    struct drm_mode_fb_cmd *mode_cmd,
index aaabbcbe590507ca7b56dc382cc09370bd8e449c..8cd791dc5b298d1d11217429798f8f66f0ff0089 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/delay.h>
 #include <linux/fb.h>
 #include <linux/init.h>
+#include <linux/vga_switcheroo.h>
 
 #include "drmP.h"
 #include "drm.h"
@@ -235,6 +236,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
                        obj_priv->gtt_offset, fbo);
 
        mutex_unlock(&dev->struct_mutex);
+       vga_switcheroo_client_fb_set(dev->pdev, info);
        return 0;
 
 out_unpin:
index 0e268deed761e6e6de2fe817f96f9b330e414d0c..a30f8bfc198563d3f8beb8634b82e8dc6bb6a915 100644 (file)
@@ -82,7 +82,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
        /* HW workaround, need to toggle enable bit off and on for 12bpc, but
         * we do this anyway which shows more stable in testing.
         */
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE);
                POSTING_READ(hdmi_priv->sdvox_reg);
        }
@@ -99,7 +99,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
        /* HW workaround, need to write this twice for issue that may result
         * in first write getting masked.
         */
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                I915_WRITE(hdmi_priv->sdvox_reg, temp);
                POSTING_READ(hdmi_priv->sdvox_reg);
        }
index 8673c735b8ab1b7fc669d5c2fd730f06d95ff66d..fcc753ca5d9446434e026197ba8318cbcfbda9b8 100644 (file)
@@ -128,7 +128,7 @@ intel_i2c_reset_gmbus(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                I915_WRITE(PCH_GMBUS0, 0);
        } else {
                I915_WRITE(GMBUS0, 0);
index c2e8a45780d558b44b3e0453d0ec328c9991326b..14e516fdc2dd5c71b69f403bf184450171f3f185 100644 (file)
@@ -56,7 +56,7 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 blc_pwm_ctl, reg;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                reg = BLC_PWM_CPU_CTL;
        else
                reg = BLC_PWM_CTL;
@@ -74,7 +74,7 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                reg = BLC_PWM_PCH_CTL2;
        else
                reg = BLC_PWM_CTL;
@@ -89,17 +89,22 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
 static void intel_lvds_set_power(struct drm_device *dev, bool on)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 pp_status, ctl_reg, status_reg;
+       u32 pp_status, ctl_reg, status_reg, lvds_reg;
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                ctl_reg = PCH_PP_CONTROL;
                status_reg = PCH_PP_STATUS;
+               lvds_reg = PCH_LVDS;
        } else {
                ctl_reg = PP_CONTROL;
                status_reg = PP_STATUS;
+               lvds_reg = LVDS;
        }
 
        if (on) {
+               I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
+               POSTING_READ(lvds_reg);
+
                I915_WRITE(ctl_reg, I915_READ(ctl_reg) |
                           POWER_TARGET_ON);
                do {
@@ -115,6 +120,9 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on)
                do {
                        pp_status = I915_READ(status_reg);
                } while (pp_status & PP_ON);
+
+               I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN);
+               POSTING_READ(lvds_reg);
        }
 }
 
@@ -137,7 +145,7 @@ static void intel_lvds_save(struct drm_connector *connector)
        u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
        u32 pwm_ctl_reg;
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                pp_on_reg = PCH_PP_ON_DELAYS;
                pp_off_reg = PCH_PP_OFF_DELAYS;
                pp_ctl_reg = PCH_PP_CONTROL;
@@ -174,7 +182,7 @@ static void intel_lvds_restore(struct drm_connector *connector)
        u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
        u32 pwm_ctl_reg;
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                pp_on_reg = PCH_PP_ON_DELAYS;
                pp_off_reg = PCH_PP_OFF_DELAYS;
                pp_ctl_reg = PCH_PP_CONTROL;
@@ -297,7 +305,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
        }
 
        /* full screen scale for now */
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                goto out;
 
        /* 965+ wants fuzzy fitting */
@@ -327,7 +335,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
         * to register description and PRM.
         * Change the value here to see the borders for debugging
         */
-       if (!IS_IRONLAKE(dev)) {
+       if (!HAS_PCH_SPLIT(dev)) {
                I915_WRITE(BCLRPAT_A, 0);
                I915_WRITE(BCLRPAT_B, 0);
        }
@@ -548,7 +556,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                reg = BLC_PWM_CPU_CTL;
        else
                reg = BLC_PWM_CTL;
@@ -587,7 +595,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
         * settings.
         */
 
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                return;
 
        /*
@@ -655,8 +663,15 @@ static const struct dmi_system_id bad_lid_status[] = {
  */
 static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector)
 {
+       struct drm_device *dev = connector->dev;
        enum drm_connector_status status = connector_status_connected;
 
+       /* ACPI lid methods were generally unreliable in this generation, so
+        * don't even bother.
+        */
+       if (IS_GEN2(dev))
+               return connector_status_connected;
+
        if (!dmi_check_system(bad_lid_status) && !acpi_lid_open())
                status = connector_status_disconnected;
 
@@ -1020,7 +1035,7 @@ void intel_lvds_init(struct drm_device *dev)
                return;
        }
 
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
                        return;
                if (dev_priv->edp_support) {
@@ -1123,7 +1138,7 @@ void intel_lvds_init(struct drm_device *dev)
         */
 
        /* Ironlake: FIXME if still fail, not try pipe mode now */
-       if (IS_IRONLAKE(dev))
+       if (HAS_PCH_SPLIT(dev))
                goto failed;
 
        lvds = I915_READ(LVDS);
@@ -1144,7 +1159,7 @@ void intel_lvds_init(struct drm_device *dev)
                goto failed;
 
 out:
-       if (IS_IRONLAKE(dev)) {
+       if (HAS_PCH_SPLIT(dev)) {
                u32 pwm;
                /* make sure PWM is enabled */
                pwm = I915_READ(BLC_PWM_CPU_CTL2);
index 2639591c72e97c74be2e6c4783b3be38f673ff04..d355d1d527e766438a3a177218eed2dbb9210a7d 100644 (file)
@@ -172,7 +172,7 @@ struct overlay_registers {
 #define OFC_UPDATE             0x1
 
 #define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev))
-#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev))
+#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev) && !IS_GEN6(dev))
 
 
 static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
@@ -199,16 +199,11 @@ static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_over
 
 static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay)
 {
-       struct drm_device *dev = overlay->dev;
-        drm_i915_private_t *dev_priv = dev->dev_private;
-
        if (OVERLAY_NONPHYSICAL(overlay->dev))
                io_mapping_unmap_atomic(overlay->virt_addr);
 
        overlay->virt_addr = NULL;
 
-       I915_READ(OVADD); /* flush wc cashes */
-
        return;
 }
 
@@ -225,9 +220,7 @@ static int intel_overlay_on(struct intel_overlay *overlay)
        overlay->active = 1;
        overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP;
 
-       BEGIN_LP_RING(6);
-       OUT_RING(MI_FLUSH);
-       OUT_RING(MI_NOOP);
+       BEGIN_LP_RING(4);
        OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON);
        OUT_RING(overlay->flip_addr | OFC_UPDATE);
        OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
@@ -267,9 +260,7 @@ static void intel_overlay_continue(struct intel_overlay *overlay,
        if (tmp & (1 << 17))
                DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
 
-       BEGIN_LP_RING(4);
-       OUT_RING(MI_FLUSH);
-       OUT_RING(MI_NOOP);
+       BEGIN_LP_RING(2);
        OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
        OUT_RING(flip_addr);
         ADVANCE_LP_RING();
@@ -338,9 +329,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
        /* wait for overlay to go idle */
        overlay->hw_wedged = SWITCH_OFF_STAGE_1;
 
-       BEGIN_LP_RING(6);
-       OUT_RING(MI_FLUSH);
-       OUT_RING(MI_NOOP);
+       BEGIN_LP_RING(4);
        OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
        OUT_RING(flip_addr);
         OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
@@ -358,9 +347,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
        /* turn overlay off */
        overlay->hw_wedged = SWITCH_OFF_STAGE_2;
 
-       BEGIN_LP_RING(6);
-        OUT_RING(MI_FLUSH);
-        OUT_RING(MI_NOOP);
+       BEGIN_LP_RING(4);
         OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
        OUT_RING(flip_addr);
         OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
@@ -435,9 +422,7 @@ int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
 
                        overlay->hw_wedged = SWITCH_OFF_STAGE_2;
 
-                       BEGIN_LP_RING(6);
-                       OUT_RING(MI_FLUSH);
-                       OUT_RING(MI_NOOP);
+                       BEGIN_LP_RING(4);
                        OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
                        OUT_RING(flip_addr);
                        OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
@@ -1179,7 +1164,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
        mutex_unlock(&dev->mode_config.mutex);
-       drm_gem_object_unreference(new_bo);
+       drm_gem_object_unreference_unlocked(new_bo);
        kfree(params);
 
        return ret;
index 82678d30ab06504ba51840f0d5c773c53f330ae8..48daee5c9c63bad7a813655ff6635784f3b6ef75 100644 (file)
@@ -35,6 +35,7 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 #include "intel_sdvo_regs.h"
+#include <linux/dmi.h>
 
 static char *tv_format_names[] = {
        "NTSC_M"   , "NTSC_J"  , "NTSC_443",
@@ -2283,6 +2284,25 @@ intel_sdvo_get_slave_addr(struct drm_device *dev, int output_device)
                return 0x72;
 }
 
+static int intel_sdvo_bad_tv_callback(const struct dmi_system_id *id)
+{
+       DRM_DEBUG_KMS("Ignoring bad SDVO TV connector for %s\n", id->ident);
+       return 1;
+}
+
+static struct dmi_system_id intel_sdvo_bad_tv[] = {
+       {
+               .callback = intel_sdvo_bad_tv_callback,
+               .ident = "IntelG45/ICH10R/DME1737",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "IBM CORPORATION"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "4800784"),
+               },
+       },
+
+       { }     /* terminating entry */
+};
+
 static bool
 intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
 {
@@ -2323,7 +2343,8 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
                                        (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
                                        (1 << INTEL_ANALOG_CLONE_BIT);
                }
-       } else if (flags & SDVO_OUTPUT_SVID0) {
+       } else if ((flags & SDVO_OUTPUT_SVID0) &&
+                  !dmi_check_system(intel_sdvo_bad_tv)) {
 
                sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0;
                encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
index 48c290b5da8c2743257cdfa9ca08db565dd7bd7b..32db806f3b5aa53e82a25797c271b8f4ca8fa2c0 100644 (file)
@@ -16,7 +16,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
              nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
              nv04_graph.o nv10_graph.o nv20_graph.o \
              nv40_graph.o nv50_graph.o \
-             nv40_grctx.o \
+             nv40_grctx.o nv50_grctx.o \
              nv04_instmem.o nv50_instmem.o \
              nv50_crtc.o nv50_dac.o nv50_sor.o \
              nv50_cursor.o nv50_display.o nv50_fbcon.o \
index 48227e7447539f281ffc79e40ebd05b517d0c1ae..0e0730a531371e9e59ec01d1db10b72143c0c67b 100644 (file)
@@ -11,6 +11,8 @@
 #include "nouveau_drm.h"
 #include "nv50_display.h"
 
+#include <linux/vga_switcheroo.h>
+
 #define NOUVEAU_DSM_SUPPORTED 0x00
 #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
 
 #define NOUVEAU_DSM_POWER_SPEED 0x01
 #define NOUVEAU_DSM_POWER_STAMINA 0x02
 
-static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
-{
-       static char muid[] = {
-               0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
-               0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
-       };
+static struct nouveau_dsm_priv {
+       bool dsm_detected;
+       acpi_handle dhandle;
+       acpi_handle dsm_handle;
+} nouveau_dsm_priv;
+
+static const char nouveau_dsm_muid[] = {
+       0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
+       0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
+};
 
-       struct pci_dev *pdev = dev->pdev;
-       struct acpi_handle *handle;
+static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result)
+{
        struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
        struct acpi_object_list input;
        union acpi_object params[4];
        union acpi_object *obj;
        int err;
 
-       handle = DEVICE_ACPI_HANDLE(&pdev->dev);
-
-       if (!handle)
-               return -ENODEV;
-
        input.count = 4;
        input.pointer = params;
        params[0].type = ACPI_TYPE_BUFFER;
-       params[0].buffer.length = sizeof(muid);
-       params[0].buffer.pointer = (char *)muid;
+       params[0].buffer.length = sizeof(nouveau_dsm_muid);
+       params[0].buffer.pointer = (char *)nouveau_dsm_muid;
        params[1].type = ACPI_TYPE_INTEGER;
        params[1].integer.value = 0x00000102;
        params[2].type = ACPI_TYPE_INTEGER;
@@ -62,7 +63,7 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
 
        err = acpi_evaluate_object(handle, "_DSM", &input, &output);
        if (err) {
-               NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
+               printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
                return err;
        }
 
@@ -86,40 +87,119 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
        return 0;
 }
 
-int nouveau_hybrid_setup(struct drm_device *dev)
+static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
 {
-       int result;
-
-       if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE,
-                                                               &result))
-               return -ENODEV;
-
-       NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
-
-       if (result) { /* Ensure that the external GPU is enabled */
-               nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
-               nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
-                                                                       NULL);
-       } else { /* Stamina mode - disable the external GPU */
-               nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
-                                                                       NULL);
-               nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
-                                                                       NULL);
-       }
+       return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
+}
+
+static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
+{
+       int arg;
+       if (state == VGA_SWITCHEROO_ON)
+               arg = NOUVEAU_DSM_POWER_SPEED;
+       else
+               arg = NOUVEAU_DSM_POWER_STAMINA;
+       nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);
+       return 0;
+}
+
+static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
+{
+       if (id == VGA_SWITCHEROO_IGD)
+               return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_STAMINA);
+       else
+               return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_SPEED);
+}
 
+static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
+                                  enum vga_switcheroo_state state)
+{
+       if (id == VGA_SWITCHEROO_IGD)
+               return 0;
+
+       return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dsm_handle, state);
+}
+
+static int nouveau_dsm_init(void)
+{
        return 0;
 }
 
-bool nouveau_dsm_probe(struct drm_device *dev)
+static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
 {
-       int support = 0;
+       if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
+               return VGA_SWITCHEROO_IGD;
+       else
+               return VGA_SWITCHEROO_DIS;
+}
+
+static struct vga_switcheroo_handler nouveau_dsm_handler = {
+       .switchto = nouveau_dsm_switchto,
+       .power_state = nouveau_dsm_power_state,
+       .init = nouveau_dsm_init,
+       .get_client_id = nouveau_dsm_get_client_id,
+};
 
-       if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
-                               NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
+static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
+{
+       acpi_handle dhandle, nvidia_handle;
+       acpi_status status;
+       int ret;
+       uint32_t result;
+
+       dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       if (!dhandle)
+               return false;
+       status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
+       if (ACPI_FAILURE(status)) {
                return false;
+       }
 
-       if (!support)
+       ret= nouveau_dsm(nvidia_handle, NOUVEAU_DSM_SUPPORTED,
+                        NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
+       if (ret < 0)
                return false;
 
+       nouveau_dsm_priv.dhandle = dhandle;
+       nouveau_dsm_priv.dsm_handle = nvidia_handle;
        return true;
 }
+
+static bool nouveau_dsm_detect(void)
+{
+       char acpi_method_name[255] = { 0 };
+       struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
+       struct pci_dev *pdev = NULL;
+       int has_dsm = 0;
+       int vga_count = 0;
+       while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
+               vga_count++;
+
+               has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
+       }
+
+       if (vga_count == 2 && has_dsm) {
+               acpi_get_name(nouveau_dsm_priv.dsm_handle, ACPI_FULL_PATHNAME, &buffer);
+               printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
+                      acpi_method_name);
+               nouveau_dsm_priv.dsm_detected = true;
+               return true;
+       }
+       return false;
+}
+
+void nouveau_register_dsm_handler(void)
+{
+       bool r;
+
+       r = nouveau_dsm_detect();
+       if (!r)
+               return;
+
+       vga_switcheroo_register_handler(&nouveau_dsm_handler);
+}
+
+void nouveau_unregister_dsm_handler(void)
+{
+       vga_switcheroo_unregister_handler();
+}
index 0e9cd1d49130657c4aee725b8b490e75f0b4ba19..71247da17da5cd4e820dbfe953b61f524cce52c4 100644 (file)
@@ -311,11 +311,11 @@ valid_reg(struct nvbios *bios, uint32_t reg)
 
        /* C51 has misaligned regs on purpose. Marvellous */
        if (reg & 0x2 ||
-           (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51))
+           (reg & 0x1 && dev_priv->vbios.chip_version != 0x51))
                NV_ERROR(dev, "======= misaligned reg 0x%08X =======\n", reg);
 
        /* warn on C51 regs that haven't been verified accessible in tracing */
-       if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 &&
+       if (reg & 0x1 && dev_priv->vbios.chip_version == 0x51 &&
            reg != 0x130d && reg != 0x1311 && reg != 0x60081d)
                NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n",
                        reg);
@@ -420,7 +420,7 @@ bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data)
        LOG_OLD_VALUE(bios_rd32(bios, reg));
        BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
 
-       if (dev_priv->VBIOS.execute) {
+       if (dev_priv->vbios.execute) {
                still_alive();
                nv_wr32(bios->dev, reg, data);
        }
@@ -647,7 +647,7 @@ nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
        reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16);
        reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1;
 
-       if (dev_priv->VBIOS.execute) {
+       if (dev_priv->vbios.execute) {
                still_alive();
                nv_wr32(dev, reg + 4, reg1);
                nv_wr32(dev, reg + 0, reg0);
@@ -689,7 +689,7 @@ setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
 static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
 
        /*
         * For the results of this function to be correct, CR44 must have been
@@ -700,7 +700,7 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
 
        uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0);
 
-       if (dcb_entry > bios->bdcb.dcb.entries) {
+       if (dcb_entry > bios->dcb.entries) {
                NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently "
                                "(%02X)\n", dcb_entry);
                dcb_entry = 0x7f;       /* unused / invalid marker */
@@ -713,25 +713,26 @@ static struct nouveau_i2c_chan *
 init_i2c_device_find(struct drm_device *dev, int i2c_index)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb;
+       struct dcb_table *dcb = &dev_priv->vbios.dcb;
 
        if (i2c_index == 0xff) {
                /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
                int idx = dcb_entry_idx_from_crtchead(dev), shift = 0;
-               int default_indices = bdcb->i2c_default_indices;
+               int default_indices = dcb->i2c_default_indices;
 
-               if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default)
+               if (idx != 0x7f && dcb->entry[idx].i2c_upper_default)
                        shift = 4;
 
                i2c_index = (default_indices >> shift) & 0xf;
        }
        if (i2c_index == 0x80)  /* g80+ */
-               i2c_index = bdcb->i2c_default_indices & 0xf;
+               i2c_index = dcb->i2c_default_indices & 0xf;
 
        return nouveau_i2c_find(dev, i2c_index);
 }
 
-static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
+static uint32_t
+get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
 {
        /*
         * For mlv < 0x80, it is an index into a table of TMDS base addresses.
@@ -744,6 +745,7 @@ static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
         */
 
        struct drm_nouveau_private *dev_priv = dev->dev_private;
+       struct nvbios *bios = &dev_priv->vbios;
        const int pramdac_offset[13] = {
                0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
        const uint32_t pramdac_table[4] = {
@@ -756,13 +758,12 @@ static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
                dcb_entry = dcb_entry_idx_from_crtchead(dev);
                if (dcb_entry == 0x7f)
                        return 0;
-               dacoffset = pramdac_offset[
-                               dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or];
+               dacoffset = pramdac_offset[bios->dcb.entry[dcb_entry].or];
                if (mlv == 0x81)
                        dacoffset ^= 8;
                return 0x6808b0 + dacoffset;
        } else {
-               if (mlv > ARRAY_SIZE(pramdac_table)) {
+               if (mlv >= ARRAY_SIZE(pramdac_table)) {
                        NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n",
                                                                        mlv);
                        return 0;
@@ -2574,19 +2575,19 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
        const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
        const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
-       const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr];
+       const uint8_t *gpio_table = &bios->data[bios->dcb.gpio_table_ptr];
        const uint8_t *gpio_entry;
        int i;
 
        if (!iexec->execute)
                return 1;
 
-       if (bios->bdcb.version != 0x40) {
+       if (bios->dcb.version != 0x40) {
                NV_ERROR(bios->dev, "DCB table not version 4.0\n");
                return 0;
        }
 
-       if (!bios->bdcb.gpio_table_ptr) {
+       if (!bios->dcb.gpio_table_ptr) {
                NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n");
                return 0;
        }
@@ -3123,7 +3124,7 @@ run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
                      struct dcb_entry *dcbent, int head, bool dl)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        struct init_exec iexec = {true, false};
 
        NV_TRACE(dev, "0x%04X: Parsing digital output script table\n",
@@ -3140,7 +3141,7 @@ run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
 static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0);
        uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]);
 
@@ -3194,7 +3195,7 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int
         * of a list of pxclks and script pointers.
         */
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        unsigned int outputset = (dcbent->or == 4) ? 1 : 0;
        uint16_t scriptptr = 0, clktable;
        uint8_t clktableptr = 0;
@@ -3261,7 +3262,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head,
         */
 
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
        uint32_t sel_clk_binding, sel_clk;
        int ret;
@@ -3395,7 +3396,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
 #ifndef __powerpc__
                NV_ERROR(dev, "Pointer to flat panel table invalid\n");
 #endif
-               bios->pub.digital_min_front_porch = 0x4b;
+               bios->digital_min_front_porch = 0x4b;
                return 0;
        }
 
@@ -3428,7 +3429,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
                 * fptable[4] is the minimum
                 * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap
                 */
-               bios->pub.digital_min_front_porch = fptable[4];
+               bios->digital_min_front_porch = fptable[4];
                ofs = -7;
                break;
        default:
@@ -3467,7 +3468,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
 
        /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */
        if (lth.lvds_ver > 0x10)
-               bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
+               bios->fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
 
        /*
         * If either the strap or xlated fpindex value are 0xf there is no
@@ -3491,7 +3492,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
 bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr];
 
        if (!mode)      /* just checking whether we can produce a mode */
@@ -3562,11 +3563,11 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
         * until later, when this function should be called with non-zero pxclk
         */
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0;
        struct lvdstableheader lth;
        uint16_t lvdsofs;
-       int ret, chip_version = bios->pub.chip_version;
+       int ret, chip_version = bios->chip_version;
 
        ret = parse_lvds_manufacturer_table_header(dev, bios, &lth);
        if (ret)
@@ -3682,7 +3683,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
                         uint16_t record, int record_len, int record_nr)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint32_t entry;
        uint16_t table;
        int i, v;
@@ -3716,7 +3717,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
                      int *length)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint8_t *table;
 
        if (!bios->display.dp_table_ptr) {
@@ -3725,7 +3726,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
        }
        table = &bios->data[bios->display.dp_table_ptr];
 
-       if (table[0] != 0x21) {
+       if (table[0] != 0x20 && table[0] != 0x21) {
                NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
                         table[0]);
                return NULL;
@@ -3765,7 +3766,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
         */
 
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint8_t *table = &bios->data[bios->display.script_table_ptr];
        uint8_t *otable = NULL;
        uint16_t script;
@@ -3918,8 +3919,8 @@ int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, i
         */
 
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
-       int cv = bios->pub.chip_version;
+       struct nvbios *bios = &dev_priv->vbios;
+       int cv = bios->chip_version;
        uint16_t clktable = 0, scriptptr;
        uint32_t sel_clk_binding, sel_clk;
 
@@ -3978,8 +3979,8 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
         */
 
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
-       int cv = bios->pub.chip_version, pllindex = 0;
+       struct nvbios *bios = &dev_priv->vbios;
+       int cv = bios->chip_version, pllindex = 0;
        uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
        uint32_t crystal_strap_mask, crystal_straps;
 
@@ -4332,7 +4333,7 @@ static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint
         */
 
        bios->major_version = bios->data[offset + 3];
-       bios->pub.chip_version = bios->data[offset + 2];
+       bios->chip_version = bios->data[offset + 2];
        NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n",
                 bios->data[offset + 3], bios->data[offset + 2],
                 bios->data[offset + 1], bios->data[offset]);
@@ -4402,7 +4403,7 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
        }
 
        /* First entry is normal dac, 2nd tv-out perhaps? */
-       bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
+       bios->dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
 
        return 0;
 }
@@ -4526,8 +4527,8 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
                return -ENOSYS;
        }
 
-       bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
-       bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
+       bios->dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
+       bios->tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
 
        return 0;
 }
@@ -4796,11 +4797,11 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
        uint16_t legacy_scripts_offset, legacy_i2c_offset;
 
        /* load needed defaults in case we can't parse this info */
-       bios->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
-       bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
-       bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
-       bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
-       bios->pub.digital_min_front_porch = 0x4b;
+       bios->dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
+       bios->dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
+       bios->dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
+       bios->dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
+       bios->digital_min_front_porch = 0x4b;
        bios->fmaxvco = 256000;
        bios->fminvco = 128000;
        bios->fp.duallink_transition_clk = 90000;
@@ -4907,10 +4908,10 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
        bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
        bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
        bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
-       bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
-       bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
-       bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
-       bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
+       bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
+       bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
+       bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
+       bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
 
        if (bmplength > 74) {
                bios->fmaxvco = ROM32(bmp[67]);
@@ -4984,7 +4985,8 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
                else
                        NV_WARN(dev,
                                "DCB I2C table has more entries than indexable "
-                               "(%d entries, max index 15)\n", i2ctable[2]);
+                               "(%d entries, max %d)\n", i2ctable[2],
+                               DCB_MAX_NUM_I2C_ENTRIES);
                entry_len = i2ctable[3];
                /* [4] is i2c_default_indices, read in parse_dcb_table() */
        }
@@ -5000,8 +5002,8 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
 
        if (index == 0xf)
                return 0;
-       if (index > i2c_entries) {
-               NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n",
+       if (index >= i2c_entries) {
+               NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
                         index, i2ctable[2]);
                return -ENOENT;
        }
@@ -5036,7 +5038,7 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
 static struct dcb_gpio_entry *
 new_gpio_entry(struct nvbios *bios)
 {
-       struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio;
+       struct dcb_gpio_table *gpio = &bios->dcb.gpio;
 
        return &gpio->entry[gpio->entries++];
 }
@@ -5045,14 +5047,14 @@ struct dcb_gpio_entry *
 nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        int i;
 
-       for (i = 0; i < bios->bdcb.gpio.entries; i++) {
-               if (bios->bdcb.gpio.entry[i].tag != tag)
+       for (i = 0; i < bios->dcb.gpio.entries; i++) {
+               if (bios->dcb.gpio.entry[i].tag != tag)
                        continue;
 
-               return &bios->bdcb.gpio.entry[i];
+               return &bios->dcb.gpio.entry[i];
        }
 
        return NULL;
@@ -5100,7 +5102,7 @@ static void
 parse_dcb_gpio_table(struct nvbios *bios)
 {
        struct drm_device *dev = bios->dev;
-       uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr;
+       uint16_t gpio_table_ptr = bios->dcb.gpio_table_ptr;
        uint8_t *gpio_table = &bios->data[gpio_table_ptr];
        int header_len = gpio_table[1],
            entries = gpio_table[2],
@@ -5108,7 +5110,7 @@ parse_dcb_gpio_table(struct nvbios *bios)
        void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
        int i;
 
-       if (bios->bdcb.version >= 0x40) {
+       if (bios->dcb.version >= 0x40) {
                if (gpio_table_ptr && entry_len != 4) {
                        NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
                        return;
@@ -5116,7 +5118,7 @@ parse_dcb_gpio_table(struct nvbios *bios)
 
                parse_entry = parse_dcb40_gpio_entry;
 
-       } else if (bios->bdcb.version >= 0x30) {
+       } else if (bios->dcb.version >= 0x30) {
                if (gpio_table_ptr && entry_len != 2) {
                        NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
                        return;
@@ -5124,7 +5126,7 @@ parse_dcb_gpio_table(struct nvbios *bios)
 
                parse_entry = parse_dcb30_gpio_entry;
 
-       } else if (bios->bdcb.version >= 0x22) {
+       } else if (bios->dcb.version >= 0x22) {
                /*
                 * DCBs older than v3.0 don't really have a GPIO
                 * table, instead they keep some GPIO info at fixed
@@ -5158,30 +5160,67 @@ struct dcb_connector_table_entry *
 nouveau_bios_connector_entry(struct drm_device *dev, int index)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        struct dcb_connector_table_entry *cte;
 
-       if (index >= bios->bdcb.connector.entries)
+       if (index >= bios->dcb.connector.entries)
                return NULL;
 
-       cte = &bios->bdcb.connector.entry[index];
+       cte = &bios->dcb.connector.entry[index];
        if (cte->type == 0xff)
                return NULL;
 
        return cte;
 }
 
+static enum dcb_connector_type
+divine_connector_type(struct nvbios *bios, int index)
+{
+       struct dcb_table *dcb = &bios->dcb;
+       unsigned encoders = 0, type = DCB_CONNECTOR_NONE;
+       int i;
+
+       for (i = 0; i < dcb->entries; i++) {
+               if (dcb->entry[i].connector == index)
+                       encoders |= (1 << dcb->entry[i].type);
+       }
+
+       if (encoders & (1 << OUTPUT_DP)) {
+               if (encoders & (1 << OUTPUT_TMDS))
+                       type = DCB_CONNECTOR_DP;
+               else
+                       type = DCB_CONNECTOR_eDP;
+       } else
+       if (encoders & (1 << OUTPUT_TMDS)) {
+               if (encoders & (1 << OUTPUT_ANALOG))
+                       type = DCB_CONNECTOR_DVI_I;
+               else
+                       type = DCB_CONNECTOR_DVI_D;
+       } else
+       if (encoders & (1 << OUTPUT_ANALOG)) {
+               type = DCB_CONNECTOR_VGA;
+       } else
+       if (encoders & (1 << OUTPUT_LVDS)) {
+               type = DCB_CONNECTOR_LVDS;
+       } else
+       if (encoders & (1 << OUTPUT_TV)) {
+               type = DCB_CONNECTOR_TV_0;
+       }
+
+       return type;
+}
+
 static void
 parse_dcb_connector_table(struct nvbios *bios)
 {
        struct drm_device *dev = bios->dev;
-       struct dcb_connector_table *ct = &bios->bdcb.connector;
+       struct dcb_connector_table *ct = &bios->dcb.connector;
        struct dcb_connector_table_entry *cte;
-       uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr];
+       uint8_t *conntab = &bios->data[bios->dcb.connector_table_ptr];
        uint8_t *entry;
        int i;
 
-       if (!bios->bdcb.connector_table_ptr) {
+       if (!bios->dcb.connector_table_ptr) {
                NV_DEBUG_KMS(dev, "No DCB connector table present\n");
                return;
        }
@@ -5203,6 +5242,7 @@ parse_dcb_connector_table(struct nvbios *bios)
                        cte->entry = ROM16(entry[0]);
                else
                        cte->entry = ROM32(entry[0]);
+
                cte->type  = (cte->entry & 0x000000ff) >> 0;
                cte->index = (cte->entry & 0x00000f00) >> 8;
                switch (cte->entry & 0x00033000) {
@@ -5228,10 +5268,33 @@ parse_dcb_connector_table(struct nvbios *bios)
 
                NV_INFO(dev, "  %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n",
                        i, cte->entry, cte->type, cte->index, cte->gpio_tag);
+
+               /* check for known types, fallback to guessing the type
+                * from attached encoders if we hit an unknown.
+                */
+               switch (cte->type) {
+               case DCB_CONNECTOR_VGA:
+               case DCB_CONNECTOR_TV_0:
+               case DCB_CONNECTOR_TV_1:
+               case DCB_CONNECTOR_TV_3:
+               case DCB_CONNECTOR_DVI_I:
+               case DCB_CONNECTOR_DVI_D:
+               case DCB_CONNECTOR_LVDS:
+               case DCB_CONNECTOR_DP:
+               case DCB_CONNECTOR_eDP:
+               case DCB_CONNECTOR_HDMI_0:
+               case DCB_CONNECTOR_HDMI_1:
+                       break;
+               default:
+                       cte->type = divine_connector_type(bios, cte->index);
+                       NV_WARN(dev, "unknown type, using 0x%02x", cte->type);
+                       break;
+               }
+
        }
 }
 
-static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
+static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb)
 {
        struct dcb_entry *entry = &dcb->entry[dcb->entries];
 
@@ -5241,7 +5304,7 @@ static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
        return entry;
 }
 
-static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
+static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads)
 {
        struct dcb_entry *entry = new_dcb_entry(dcb);
 
@@ -5252,7 +5315,7 @@ static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
        /* "or" mostly unused in early gen crt modesetting, 0 is fine */
 }
 
-static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
+static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads)
 {
        struct dcb_entry *entry = new_dcb_entry(dcb);
 
@@ -5279,7 +5342,7 @@ static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
 #endif
 }
 
-static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
+static void fabricate_tv_output(struct dcb_table *dcb, bool twoHeads)
 {
        struct dcb_entry *entry = new_dcb_entry(dcb);
 
@@ -5290,13 +5353,13 @@ static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
 }
 
 static bool
-parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                  uint32_t conn, uint32_t conf, struct dcb_entry *entry)
 {
        entry->type = conn & 0xf;
        entry->i2c_index = (conn >> 4) & 0xf;
        entry->heads = (conn >> 8) & 0xf;
-       if (bdcb->version >= 0x40)
+       if (dcb->version >= 0x40)
                entry->connector = (conn >> 12) & 0xf;
        entry->bus = (conn >> 16) & 0xf;
        entry->location = (conn >> 20) & 0x3;
@@ -5314,7 +5377,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
                 * Although the rest of a CRT conf dword is usually
                 * zeros, mac biosen have stuff there so we must mask
                 */
-               entry->crtconf.maxfreq = (bdcb->version < 0x30) ?
+               entry->crtconf.maxfreq = (dcb->version < 0x30) ?
                                         (conf & 0xffff) * 10 :
                                         (conf & 0xff) * 10000;
                break;
@@ -5323,7 +5386,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
                uint32_t mask;
                if (conf & 0x1)
                        entry->lvdsconf.use_straps_for_mode = true;
-               if (bdcb->version < 0x22) {
+               if (dcb->version < 0x22) {
                        mask = ~0xd;
                        /*
                         * The laptop in bug 14567 lies and claims to not use
@@ -5347,7 +5410,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
                         * Until we even try to use these on G8x, it's
                         * useless reporting unknown bits.  They all are.
                         */
-                       if (bdcb->version >= 0x40)
+                       if (dcb->version >= 0x40)
                                break;
 
                        NV_ERROR(dev, "Unknown LVDS configuration bits, "
@@ -5357,7 +5420,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
                }
        case OUTPUT_TV:
        {
-               if (bdcb->version >= 0x30)
+               if (dcb->version >= 0x30)
                        entry->tvconf.has_component_output = conf & (0x8 << 4);
                else
                        entry->tvconf.has_component_output = false;
@@ -5384,8 +5447,10 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
                break;
        case 0xe:
                /* weird g80 mobile type that "nv" treats as a terminator */
-               bdcb->dcb.entries--;
+               dcb->entries--;
                return false;
+       default:
+               break;
        }
 
        /* unsure what DCB version introduces this, 3.0? */
@@ -5396,7 +5461,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
 }
 
 static bool
-parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
+parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb,
                  uint32_t conn, uint32_t conf, struct dcb_entry *entry)
 {
        switch (conn & 0x0000000f) {
@@ -5462,27 +5527,27 @@ parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
        return true;
 }
 
-static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+static bool parse_dcb_entry(struct drm_device *dev, struct dcb_table *dcb,
                            uint32_t conn, uint32_t conf)
 {
-       struct dcb_entry *entry = new_dcb_entry(&bdcb->dcb);
+       struct dcb_entry *entry = new_dcb_entry(dcb);
        bool ret;
 
-       if (bdcb->version >= 0x20)
-               ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry);
+       if (dcb->version >= 0x20)
+               ret = parse_dcb20_entry(dev, dcb, conn, conf, entry);
        else
-               ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry);
+               ret = parse_dcb15_entry(dev, dcb, conn, conf, entry);
        if (!ret)
                return ret;
 
-       read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table,
-                          entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]);
+       read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+                          entry->i2c_index, &dcb->i2c[entry->i2c_index]);
 
        return true;
 }
 
 static
-void merge_like_dcb_entries(struct drm_device *dev, struct parsed_dcb *dcb)
+void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb)
 {
        /*
         * DCB v2.0 lists each output combination separately.
@@ -5534,8 +5599,7 @@ static int
 parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct bios_parsed_dcb *bdcb = &bios->bdcb;
-       struct parsed_dcb *dcb;
+       struct dcb_table *dcb = &bios->dcb;
        uint16_t dcbptr = 0, i2ctabptr = 0;
        uint8_t *dcbtable;
        uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
@@ -5543,9 +5607,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
        int recordlength = 8, confofs = 4;
        int i;
 
-       dcb = bios->pub.dcb = &bdcb->dcb;
-       dcb->entries = 0;
-
        /* get the offset from 0x36 */
        if (dev_priv->card_type > NV_04) {
                dcbptr = ROM16(bios->data[0x36]);
@@ -5567,21 +5628,21 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
        dcbtable = &bios->data[dcbptr];
 
        /* get DCB version */
-       bdcb->version = dcbtable[0];
+       dcb->version = dcbtable[0];
        NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
-                bdcb->version >> 4, bdcb->version & 0xf);
+                dcb->version >> 4, dcb->version & 0xf);
 
-       if (bdcb->version >= 0x20) { /* NV17+ */
+       if (dcb->version >= 0x20) { /* NV17+ */
                uint32_t sig;
 
-               if (bdcb->version >= 0x30) { /* NV40+ */
+               if (dcb->version >= 0x30) { /* NV40+ */
                        headerlen = dcbtable[1];
                        entries = dcbtable[2];
                        recordlength = dcbtable[3];
                        i2ctabptr = ROM16(dcbtable[4]);
                        sig = ROM32(dcbtable[6]);
-                       bdcb->gpio_table_ptr = ROM16(dcbtable[10]);
-                       bdcb->connector_table_ptr = ROM16(dcbtable[20]);
+                       dcb->gpio_table_ptr = ROM16(dcbtable[10]);
+                       dcb->connector_table_ptr = ROM16(dcbtable[20]);
                } else {
                        i2ctabptr = ROM16(dcbtable[2]);
                        sig = ROM32(dcbtable[4]);
@@ -5593,7 +5654,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
                                        "signature (%08X)\n", sig);
                        return -EINVAL;
                }
-       } else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */
+       } else if (dcb->version >= 0x15) { /* some NV11 and NV20 */
                char sig[8] = { 0 };
 
                strncpy(sig, (char *)&dcbtable[-7], 7);
@@ -5641,14 +5702,11 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
        if (!i2ctabptr)
                NV_WARN(dev, "No pointer to DCB I2C port table\n");
        else {
-               bdcb->i2c_table = &bios->data[i2ctabptr];
-               if (bdcb->version >= 0x30)
-                       bdcb->i2c_default_indices = bdcb->i2c_table[4];
+               dcb->i2c_table = &bios->data[i2ctabptr];
+               if (dcb->version >= 0x30)
+                       dcb->i2c_default_indices = dcb->i2c_table[4];
        }
 
-       parse_dcb_gpio_table(bios);
-       parse_dcb_connector_table(bios);
-
        if (entries > DCB_MAX_NUM_ENTRIES)
                entries = DCB_MAX_NUM_ENTRIES;
 
@@ -5673,7 +5731,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
                NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
                             dcb->entries, connection, config);
 
-               if (!parse_dcb_entry(dev, bdcb, connection, config))
+               if (!parse_dcb_entry(dev, dcb, connection, config))
                        break;
        }
 
@@ -5681,18 +5739,22 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
         * apart for v2.1+ not being known for requiring merging, this
         * guarantees dcbent->index is the index of the entry in the rom image
         */
-       if (bdcb->version < 0x21)
+       if (dcb->version < 0x21)
                merge_like_dcb_entries(dev, dcb);
 
-       return dcb->entries ? 0 : -ENXIO;
+       if (!dcb->entries)
+               return -ENXIO;
+
+       parse_dcb_gpio_table(bios);
+       parse_dcb_connector_table(bios);
+       return 0;
 }
 
 static void
 fixup_legacy_connector(struct nvbios *bios)
 {
-       struct bios_parsed_dcb *bdcb = &bios->bdcb;
-       struct parsed_dcb *dcb = &bdcb->dcb;
-       int high = 0, i;
+       struct dcb_table *dcb = &bios->dcb;
+       int i, i2c, i2c_conn[DCB_MAX_NUM_I2C_ENTRIES] = { };
 
        /*
         * DCB 3.0 also has the table in most cases, but there are some cards
@@ -5700,9 +5762,11 @@ fixup_legacy_connector(struct nvbios *bios)
         * indices are all 0.  We don't need the connector indices on pre-G80
         * chips (yet?) so limit the use to DCB 4.0 and above.
         */
-       if (bdcb->version >= 0x40)
+       if (dcb->version >= 0x40)
                return;
 
+       dcb->connector.entries = 0;
+
        /*
         * No known connector info before v3.0, so make it up.  the rule here
         * is: anything on the same i2c bus is considered to be on the same
@@ -5710,37 +5774,38 @@ fixup_legacy_connector(struct nvbios *bios)
         * its own unique connector index.
         */
        for (i = 0; i < dcb->entries; i++) {
-               if (dcb->entry[i].i2c_index == 0xf)
-                       continue;
-
                /*
                 * Ignore the I2C index for on-chip TV-out, as there
                 * are cards with bogus values (nv31m in bug 23212),
                 * and it's otherwise useless.
                 */
                if (dcb->entry[i].type == OUTPUT_TV &&
-                   dcb->entry[i].location == DCB_LOC_ON_CHIP) {
+                   dcb->entry[i].location == DCB_LOC_ON_CHIP)
                        dcb->entry[i].i2c_index = 0xf;
+               i2c = dcb->entry[i].i2c_index;
+
+               if (i2c_conn[i2c]) {
+                       dcb->entry[i].connector = i2c_conn[i2c] - 1;
                        continue;
                }
 
-               dcb->entry[i].connector = dcb->entry[i].i2c_index;
-               if (dcb->entry[i].connector > high)
-                       high = dcb->entry[i].connector;
+               dcb->entry[i].connector = dcb->connector.entries++;
+               if (i2c != 0xf)
+                       i2c_conn[i2c] = dcb->connector.entries;
        }
 
-       for (i = 0; i < dcb->entries; i++) {
-               if (dcb->entry[i].i2c_index != 0xf)
-                       continue;
-
-               dcb->entry[i].connector = ++high;
+       /* Fake the connector table as well as just connector indices */
+       for (i = 0; i < dcb->connector.entries; i++) {
+               dcb->connector.entry[i].index = i;
+               dcb->connector.entry[i].type = divine_connector_type(bios, i);
+               dcb->connector.entry[i].gpio_tag = 0xff;
        }
 }
 
 static void
 fixup_legacy_i2c(struct nvbios *bios)
 {
-       struct parsed_dcb *dcb = &bios->bdcb.dcb;
+       struct dcb_table *dcb = &bios->dcb;
        int i;
 
        for (i = 0; i < dcb->entries; i++) {
@@ -5826,7 +5891,7 @@ static int load_nv17_hw_sequencer_ucode(struct drm_device *dev,
 uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        const uint8_t edid_sig[] = {
                        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
        uint16_t offset = 0;
@@ -5859,7 +5924,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
                            struct dcb_entry *dcbent)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        struct init_exec iexec = { true, false };
 
        mutex_lock(&bios->lock);
@@ -5872,7 +5937,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
 static bool NVInitVBIOS(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
 
        memset(bios, 0, sizeof(struct nvbios));
        mutex_init(&bios->lock);
@@ -5888,7 +5953,7 @@ static bool NVInitVBIOS(struct drm_device *dev)
 static int nouveau_parse_vbios_struct(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
        const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
        int offset;
@@ -5915,7 +5980,7 @@ int
 nouveau_run_vbios_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        int i, ret = 0;
 
        NVLockVgaCrtcs(dev, false);
@@ -5946,9 +6011,9 @@ nouveau_run_vbios_init(struct drm_device *dev)
        }
 
        if (dev_priv->card_type >= NV_50) {
-               for (i = 0; i < bios->bdcb.dcb.entries; i++) {
+               for (i = 0; i < bios->dcb.entries; i++) {
                        nouveau_bios_run_display_table(dev,
-                                                      &bios->bdcb.dcb.entry[i],
+                                                      &bios->dcb.entry[i],
                                                       0, 0);
                }
        }
@@ -5962,11 +6027,11 @@ static void
 nouveau_bios_i2c_devices_takedown(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        struct dcb_i2c_entry *entry;
        int i;
 
-       entry = &bios->bdcb.dcb.i2c[0];
+       entry = &bios->dcb.i2c[0];
        for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++)
                nouveau_i2c_fini(dev, entry);
 }
@@ -5975,13 +6040,11 @@ int
 nouveau_bios_init(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint32_t saved_nv_pextdev_boot_0;
        bool was_locked;
        int ret;
 
-       dev_priv->vbios = &bios->pub;
-
        if (!NVInitVBIOS(dev))
                return -ENODEV;
 
@@ -6023,10 +6086,8 @@ nouveau_bios_init(struct drm_device *dev)
        bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
 
        ret = nouveau_run_vbios_init(dev);
-       if (ret) {
-               dev_priv->vbios = NULL;
+       if (ret)
                return ret;
-       }
 
        /* feature_byte on BMP is poor, but init always sets CR4B */
        was_locked = NVLockVgaCrtcs(dev, false);
index fd94bd6dc2642221c572e698d0dab87402907b7d..9f688aa9a65559cb3c1febd4183e8c15b8032396 100644 (file)
 
 #define DCB_LOC_ON_CHIP 0
 
+struct dcb_i2c_entry {
+       uint8_t port_type;
+       uint8_t read, write;
+       struct nouveau_i2c_chan *chan;
+};
+
+enum dcb_gpio_tag {
+       DCB_GPIO_TVDAC0 = 0xc,
+       DCB_GPIO_TVDAC1 = 0x2d,
+};
+
+struct dcb_gpio_entry {
+       enum dcb_gpio_tag tag;
+       int line;
+       bool invert;
+};
+
+struct dcb_gpio_table {
+       int entries;
+       struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
+};
+
+enum dcb_connector_type {
+       DCB_CONNECTOR_VGA = 0x00,
+       DCB_CONNECTOR_TV_0 = 0x10,
+       DCB_CONNECTOR_TV_1 = 0x11,
+       DCB_CONNECTOR_TV_3 = 0x13,
+       DCB_CONNECTOR_DVI_I = 0x30,
+       DCB_CONNECTOR_DVI_D = 0x31,
+       DCB_CONNECTOR_LVDS = 0x40,
+       DCB_CONNECTOR_DP = 0x46,
+       DCB_CONNECTOR_eDP = 0x47,
+       DCB_CONNECTOR_HDMI_0 = 0x60,
+       DCB_CONNECTOR_HDMI_1 = 0x61,
+       DCB_CONNECTOR_NONE = 0xff
+};
+
+struct dcb_connector_table_entry {
+       uint32_t entry;
+       enum dcb_connector_type type;
+       uint8_t index;
+       uint8_t gpio_tag;
+};
+
+struct dcb_connector_table {
+       int entries;
+       struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
+};
+
+enum dcb_type {
+       OUTPUT_ANALOG = 0,
+       OUTPUT_TV = 1,
+       OUTPUT_TMDS = 2,
+       OUTPUT_LVDS = 3,
+       OUTPUT_DP = 6,
+       OUTPUT_ANY = -1
+};
+
 struct dcb_entry {
        int index;      /* may not be raw dcb index if merging has happened */
-       uint8_t type;
+       enum dcb_type type;
        uint8_t i2c_index;
        uint8_t heads;
        uint8_t connector;
@@ -71,69 +129,22 @@ struct dcb_entry {
        bool i2c_upper_default;
 };
 
-struct dcb_i2c_entry {
-       uint8_t port_type;
-       uint8_t read, write;
-       struct nouveau_i2c_chan *chan;
-};
+struct dcb_table {
+       uint8_t version;
 
-struct parsed_dcb {
        int entries;
        struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
-       struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
-};
-
-enum dcb_gpio_tag {
-       DCB_GPIO_TVDAC0 = 0xc,
-       DCB_GPIO_TVDAC1 = 0x2d,
-};
-
-struct dcb_gpio_entry {
-       enum dcb_gpio_tag tag;
-       int line;
-       bool invert;
-};
-
-struct parsed_dcb_gpio {
-       int entries;
-       struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
-};
-
-struct dcb_connector_table_entry {
-       uint32_t entry;
-       uint8_t type;
-       uint8_t index;
-       uint8_t gpio_tag;
-};
-
-struct dcb_connector_table {
-       int entries;
-       struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
-};
-
-struct bios_parsed_dcb {
-       uint8_t version;
-
-       struct parsed_dcb dcb;
 
        uint8_t *i2c_table;
        uint8_t i2c_default_indices;
+       struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
 
        uint16_t gpio_table_ptr;
-       struct parsed_dcb_gpio gpio;
+       struct dcb_gpio_table gpio;
        uint16_t connector_table_ptr;
        struct dcb_connector_table connector;
 };
 
-enum nouveau_encoder_type {
-       OUTPUT_ANALOG = 0,
-       OUTPUT_TV = 1,
-       OUTPUT_TMDS = 2,
-       OUTPUT_LVDS = 3,
-       OUTPUT_DP = 6,
-       OUTPUT_ANY = -1
-};
-
 enum nouveau_or {
        OUTPUT_A = (1 << 0),
        OUTPUT_B = (1 << 1),
@@ -190,8 +201,8 @@ struct pll_lims {
        int refclk;
 };
 
-struct nouveau_bios_info {
-       struct parsed_dcb *dcb;
+struct nvbios {
+       struct drm_device *dev;
 
        uint8_t chip_version;
 
@@ -199,11 +210,6 @@ struct nouveau_bios_info {
        uint32_t tvdactestval;
        uint8_t digital_min_front_porch;
        bool fp_no_ddc;
-};
-
-struct nvbios {
-       struct drm_device *dev;
-       struct nouveau_bios_info pub;
 
        struct mutex lock;
 
@@ -234,7 +240,7 @@ struct nvbios {
        uint16_t some_script_ptr; /* BIT I + 14 */
        uint16_t init96_tbl_ptr; /* BIT I + 16 */
 
-       struct bios_parsed_dcb bdcb;
+       struct dcb_table dcb;
 
        struct {
                int crtchead;
index ee2b84504d050f2fd68f66a8254c366485816405..88f9bc0941eb293a7cf727f261740900da5e14ba 100644 (file)
@@ -274,7 +274,7 @@ getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
         * returns calculated clock
         */
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int cv = dev_priv->vbios->chip_version;
+       int cv = dev_priv->vbios.chip_version;
        int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
        int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
        int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
@@ -373,7 +373,7 @@ getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
         * returns calculated clock
         */
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int chip_version = dev_priv->vbios->chip_version;
+       int chip_version = dev_priv->vbios.chip_version;
        int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
        int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
        int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
index 2281f99da7fcef31da401ea5d31811e78e93cb93..6dfb425cbae9432f4718ae33c77b754c4ac9a8dc 100644 (file)
@@ -35,22 +35,27 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_bo *pb = chan->pushbuf_bo;
        struct nouveau_gpuobj *pushbuf = NULL;
-       uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT;
        int ret;
 
+       if (dev_priv->card_type >= NV_50) {
+               ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
+                                            dev_priv->vm_end, NV_DMA_ACCESS_RO,
+                                            NV_DMA_TARGET_AGP, &pushbuf);
+               chan->pushbuf_base = pb->bo.offset;
+       } else
        if (pb->bo.mem.mem_type == TTM_PL_TT) {
                ret = nouveau_gpuobj_gart_dma_new(chan, 0,
                                                  dev_priv->gart_info.aper_size,
                                                  NV_DMA_ACCESS_RO, &pushbuf,
                                                  NULL);
-               chan->pushbuf_base = start;
+               chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT;
        } else
        if (dev_priv->card_type != NV_04) {
                ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
                                             dev_priv->fb_available_size,
                                             NV_DMA_ACCESS_RO,
                                             NV_DMA_TARGET_VIDMEM, &pushbuf);
-               chan->pushbuf_base = start;
+               chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT;
        } else {
                /* NV04 cmdbuf hack, from original ddx.. not sure of it's
                 * exact reason for existing :)  PCI access to cmdbuf in
@@ -61,7 +66,7 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
                                             dev_priv->fb_available_size,
                                             NV_DMA_ACCESS_RO,
                                             NV_DMA_TARGET_PCI, &pushbuf);
-               chan->pushbuf_base = start;
+               chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT;
        }
 
        ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
@@ -275,9 +280,18 @@ nouveau_channel_free(struct nouveau_channel *chan)
         */
        nouveau_fence_fini(chan);
 
-       /* Ensure the channel is no longer active on the GPU */
+       /* This will prevent pfifo from switching channels. */
        pfifo->reassign(dev, false);
 
+       /* We want to give pgraph a chance to idle and get rid of all potential
+        * errors. We need to do this before the lock, otherwise the irq handler
+        * is unable to process them.
+        */
+       if (pgraph->channel(dev) == chan)
+               nouveau_wait_for_idle(dev);
+
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        pgraph->fifo_access(dev, false);
        if (pgraph->channel(dev) == chan)
                pgraph->unload_context(dev);
@@ -293,6 +307,8 @@ nouveau_channel_free(struct nouveau_channel *chan)
 
        pfifo->reassign(dev, true);
 
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
        /* Release the channel's resources */
        nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
        if (chan->pushbuf_bo) {
@@ -369,6 +385,14 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
                return ret;
        init->channel  = chan->id;
 
+       if (chan->dma.ib_max)
+               init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
+                                       NOUVEAU_GEM_DOMAIN_GART;
+       else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM)
+               init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
+       else
+               init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
+
        init->subchan[0].handle = NvM2MF;
        if (dev_priv->card_type < NV_50)
                init->subchan[0].grclass = 0x0039;
@@ -408,7 +432,6 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
  ***********************************/
 
 struct drm_ioctl_desc nouveau_ioctls[] = {
-       DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
        DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH),
@@ -418,13 +441,9 @@ struct drm_ioctl_desc nouveau_ioctls[] = {
        DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH),
 };
 
 int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);
index d2f63353ea9715f3be342d369e282beec55d86fb..24327f468c4b864b459fc0af3034c6548c2d3840 100644 (file)
@@ -218,7 +218,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
                        connector->interlace_allowed = true;
        }
 
-       if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+       if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
                drm_connector_property_set_value(connector,
                        dev->mode_config.dvi_i_subconnector_property,
                        nv_encoder->dcb->type == OUTPUT_TMDS ?
@@ -236,15 +236,17 @@ nouveau_connector_detect(struct drm_connector *connector)
        struct nouveau_i2c_chan *i2c;
        int type, flags;
 
-       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+       if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS)
                nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
        if (nv_encoder && nv_connector->native_mode) {
+               unsigned status = connector_status_connected;
+
 #ifdef CONFIG_ACPI
                if (!nouveau_ignorelid && !acpi_lid_open())
-                       return connector_status_disconnected;
+                       status = connector_status_unknown;
 #endif
                nouveau_connector_set_encoder(connector, nv_encoder);
-               return connector_status_connected;
+               return status;
        }
 
        /* Cleanup the previous EDID block. */
@@ -279,7 +281,7 @@ nouveau_connector_detect(struct drm_connector *connector)
                 * same i2c channel so the value returned from ddc_detect
                 * isn't necessarily correct.
                 */
-               if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+               if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
                        if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
                                type = OUTPUT_TMDS;
                        else
@@ -321,11 +323,11 @@ detect_analog:
 static void
 nouveau_connector_force(struct drm_connector *connector)
 {
-       struct drm_device *dev = connector->dev;
+       struct nouveau_connector *nv_connector = nouveau_connector(connector);
        struct nouveau_encoder *nv_encoder;
        int type;
 
-       if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+       if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
                if (connector->force == DRM_FORCE_ON_DIGITAL)
                        type = OUTPUT_TMDS;
                else
@@ -335,7 +337,7 @@ nouveau_connector_force(struct drm_connector *connector)
 
        nv_encoder = find_encoder_by_type(connector, type);
        if (!nv_encoder) {
-               NV_ERROR(dev, "can't find encoder to force %s on!\n",
+               NV_ERROR(connector->dev, "can't find encoder to force %s on!\n",
                         drm_get_connector_name(connector));
                connector->status = connector_status_disconnected;
                return;
@@ -369,7 +371,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
                }
 
                /* LVDS always needs gpu scaling */
-               if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
+               if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS &&
                    value == DRM_MODE_SCALE_NONE)
                        return -EINVAL;
 
@@ -535,7 +537,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
        /* If we're not LVDS, destroy the previous native mode, the attached
         * monitor could have changed.
         */
-       if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
+       if (nv_connector->dcb->type != DCB_CONNECTOR_LVDS &&
            nv_connector->native_mode) {
                drm_mode_destroy(dev, nv_connector->native_mode);
                nv_connector->native_mode = NULL;
@@ -563,7 +565,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
                ret = get_slave_funcs(nv_encoder)->
                        get_modes(to_drm_encoder(nv_encoder), connector);
 
-       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+       if (nv_encoder->dcb->type == OUTPUT_LVDS)
                ret += nouveau_connector_scaler_modes_add(connector);
 
        return ret;
@@ -613,6 +615,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
 
                clock *= 3;
                break;
+       default:
+               BUG_ON(1);
+               return MODE_BAD;
        }
 
        if (clock < min_clock)
@@ -680,7 +685,7 @@ nouveau_connector_create_lvds(struct drm_device *dev,
        /* Firstly try getting EDID over DDC, if allowed and I2C channel
         * is available.
         */
-       if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
+       if (!dev_priv->vbios.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
                i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
 
        if (i2c) {
@@ -695,7 +700,7 @@ nouveau_connector_create_lvds(struct drm_device *dev,
         */
        if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
             (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
-             dev_priv->VBIOS.pub.fp_no_ddc)) {
+             dev_priv->vbios.fp_no_ddc)) {
                nv_connector->native_mode = drm_mode_duplicate(dev, &native);
                goto out;
        }
@@ -704,7 +709,7 @@ nouveau_connector_create_lvds(struct drm_device *dev,
         * stored for the panel stored in them.
         */
        if (!nv_connector->edid && !nv_connector->native_mode &&
-           !dev_priv->VBIOS.pub.fp_no_ddc) {
+           !dev_priv->vbios.fp_no_ddc) {
                struct edid *edid =
                        (struct edid *)nouveau_bios_embedded_edid(dev);
                if (edid) {
@@ -739,46 +744,66 @@ out:
 }
 
 int
-nouveau_connector_create(struct drm_device *dev, int index, int type)
+nouveau_connector_create(struct drm_device *dev,
+                        struct dcb_connector_table_entry *dcb)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_connector *nv_connector = NULL;
        struct drm_connector *connector;
        struct drm_encoder *encoder;
-       int ret;
+       int ret, type;
 
        NV_DEBUG_KMS(dev, "\n");
 
-       nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
-       if (!nv_connector)
-               return -ENOMEM;
-       nv_connector->dcb = nouveau_bios_connector_entry(dev, index);
-       connector = &nv_connector->base;
-
-       switch (type) {
-       case DRM_MODE_CONNECTOR_VGA:
+       switch (dcb->type) {
+       case DCB_CONNECTOR_NONE:
+               return 0;
+       case DCB_CONNECTOR_VGA:
                NV_INFO(dev, "Detected a VGA connector\n");
+               type = DRM_MODE_CONNECTOR_VGA;
                break;
-       case DRM_MODE_CONNECTOR_DVID:
-               NV_INFO(dev, "Detected a DVI-D connector\n");
+       case DCB_CONNECTOR_TV_0:
+       case DCB_CONNECTOR_TV_1:
+       case DCB_CONNECTOR_TV_3:
+               NV_INFO(dev, "Detected a TV connector\n");
+               type = DRM_MODE_CONNECTOR_TV;
                break;
-       case DRM_MODE_CONNECTOR_DVII:
+       case DCB_CONNECTOR_DVI_I:
                NV_INFO(dev, "Detected a DVI-I connector\n");
+               type = DRM_MODE_CONNECTOR_DVII;
                break;
-       case DRM_MODE_CONNECTOR_LVDS:
-               NV_INFO(dev, "Detected a LVDS connector\n");
+       case DCB_CONNECTOR_DVI_D:
+               NV_INFO(dev, "Detected a DVI-D connector\n");
+               type = DRM_MODE_CONNECTOR_DVID;
                break;
-       case DRM_MODE_CONNECTOR_TV:
-               NV_INFO(dev, "Detected a TV connector\n");
+       case DCB_CONNECTOR_HDMI_0:
+       case DCB_CONNECTOR_HDMI_1:
+               NV_INFO(dev, "Detected a HDMI connector\n");
+               type = DRM_MODE_CONNECTOR_HDMIA;
+               break;
+       case DCB_CONNECTOR_LVDS:
+               NV_INFO(dev, "Detected a LVDS connector\n");
+               type = DRM_MODE_CONNECTOR_LVDS;
                break;
-       case DRM_MODE_CONNECTOR_DisplayPort:
+       case DCB_CONNECTOR_DP:
                NV_INFO(dev, "Detected a DisplayPort connector\n");
+               type = DRM_MODE_CONNECTOR_DisplayPort;
                break;
-       default:
-               NV_ERROR(dev, "Unknown connector, this is not good.\n");
+       case DCB_CONNECTOR_eDP:
+               NV_INFO(dev, "Detected an eDP connector\n");
+               type = DRM_MODE_CONNECTOR_eDP;
                break;
+       default:
+               NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type);
+               return -EINVAL;
        }
 
+       nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
+       if (!nv_connector)
+               return -ENOMEM;
+       nv_connector->dcb = dcb;
+       connector = &nv_connector->base;
+
        /* defaults, will get overridden in detect() */
        connector->interlace_allowed = false;
        connector->doublescan_allowed = false;
@@ -786,55 +811,65 @@ nouveau_connector_create(struct drm_device *dev, int index, int type)
        drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
        drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
 
+       /* attach encoders */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+               if (nv_encoder->dcb->connector != dcb->index)
+                       continue;
+
+               if (get_slave_funcs(nv_encoder))
+                       get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
+
+               drm_mode_connector_attach_encoder(connector, encoder);
+       }
+
+       if (!connector->encoder_ids[0]) {
+               NV_WARN(dev, "  no encoders, ignoring\n");
+               drm_connector_cleanup(connector);
+               kfree(connector);
+               return 0;
+       }
+
        /* Init DVI-I specific properties */
-       if (type == DRM_MODE_CONNECTOR_DVII) {
+       if (dcb->type == DCB_CONNECTOR_DVI_I) {
                drm_mode_create_dvi_i_properties(dev);
                drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
                drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
        }
 
-       if (type != DRM_MODE_CONNECTOR_LVDS)
+       if (dcb->type != DCB_CONNECTOR_LVDS)
                nv_connector->use_dithering = false;
 
-       if (type == DRM_MODE_CONNECTOR_DVID ||
-           type == DRM_MODE_CONNECTOR_DVII ||
-           type == DRM_MODE_CONNECTOR_LVDS ||
-           type == DRM_MODE_CONNECTOR_DisplayPort) {
-               nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
-
-               drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property,
-                                             nv_connector->scaling_mode);
-               drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property,
-                                             nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
-                                             : DRM_MODE_DITHERING_OFF);
-
-       } else {
-               nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
-
-               if (type == DRM_MODE_CONNECTOR_VGA  &&
-                               dev_priv->card_type >= NV_50) {
+       switch (dcb->type) {
+       case DCB_CONNECTOR_VGA:
+               if (dev_priv->card_type >= NV_50) {
                        drm_connector_attach_property(connector,
                                        dev->mode_config.scaling_mode_property,
                                        nv_connector->scaling_mode);
                }
-       }
-
-       /* attach encoders */
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-
-               if (nv_encoder->dcb->connector != index)
-                       continue;
-
-               if (get_slave_funcs(nv_encoder))
-                       get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
+               /* fall-through */
+       case DCB_CONNECTOR_TV_0:
+       case DCB_CONNECTOR_TV_1:
+       case DCB_CONNECTOR_TV_3:
+               nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
+               break;
+       default:
+               nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
 
-               drm_mode_connector_attach_encoder(connector, encoder);
+               drm_connector_attach_property(connector,
+                               dev->mode_config.scaling_mode_property,
+                               nv_connector->scaling_mode);
+               drm_connector_attach_property(connector,
+                               dev->mode_config.dithering_mode_property,
+                               nv_connector->use_dithering ?
+                               DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
+               break;
        }
 
        drm_sysfs_connector_add(connector);
 
-       if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+       if (dcb->type == DCB_CONNECTOR_LVDS) {
                ret = nouveau_connector_create_lvds(dev, connector);
                if (ret) {
                        connector->funcs->destroy(connector);
index 728b8090e5ff87ed39f3f91337344dbbe727e011..4ef38abc2d9cfa85b2f569b22e01d4c3f596c753 100644 (file)
@@ -49,6 +49,7 @@ static inline struct nouveau_connector *nouveau_connector(
        return container_of(con, struct nouveau_connector, base);
 }
 
-int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
+int nouveau_connector_create(struct drm_device *,
+                            struct dcb_connector_table_entry *);
 
 #endif /* __NOUVEAU_CONNECTOR_H__ */
index d79db3698f16699e5edd3a9432357cb7b1cc10f5..8ff9ef5d4b47d1bd1df4287e0c4e109b4aaed52a 100644 (file)
@@ -47,12 +47,23 @@ nouveau_debugfs_channel_info(struct seq_file *m, void *data)
        seq_printf(m, "           cur: 0x%08x\n", chan->dma.cur << 2);
        seq_printf(m, "           put: 0x%08x\n", chan->dma.put << 2);
        seq_printf(m, "          free: 0x%08x\n", chan->dma.free << 2);
+       if (chan->dma.ib_max) {
+               seq_printf(m, "        ib max: 0x%08x\n", chan->dma.ib_max);
+               seq_printf(m, "        ib put: 0x%08x\n", chan->dma.ib_put);
+               seq_printf(m, "       ib free: 0x%08x\n", chan->dma.ib_free);
+       }
 
        seq_printf(m, "gpu fifo state:\n");
        seq_printf(m, "           get: 0x%08x\n",
                                        nvchan_rd32(chan, chan->user_get));
        seq_printf(m, "           put: 0x%08x\n",
                                        nvchan_rd32(chan, chan->user_put));
+       if (chan->dma.ib_max) {
+               seq_printf(m, "        ib get: 0x%08x\n",
+                          nvchan_rd32(chan, 0x88));
+               seq_printf(m, "        ib put: 0x%08x\n",
+                          nvchan_rd32(chan, 0x8c));
+       }
 
        seq_printf(m, "last fence    : %d\n", chan->fence.sequence);
        seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack);
@@ -133,9 +144,22 @@ nouveau_debugfs_memory_info(struct seq_file *m, void *data)
        return 0;
 }
 
+static int
+nouveau_debugfs_vbios_image(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct drm_nouveau_private *dev_priv = node->minor->dev->dev_private;
+       int i;
+
+       for (i = 0; i < dev_priv->vbios.length; i++)
+               seq_printf(m, "%c", dev_priv->vbios.data[i]);
+       return 0;
+}
+
 static struct drm_info_list nouveau_debugfs_list[] = {
        { "chipset", nouveau_debugfs_chipset_info, 0, NULL },
        { "memory", nouveau_debugfs_memory_info, 0, NULL },
+       { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL },
 };
 #define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
 
index dfc94391d71edafddb16df529f3efc054f5d011c..cf1c5c0a0abea357c1725d5649a12a822343edf1 100644 (file)
@@ -39,11 +39,8 @@ nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
        if (drm_fb->fbdev)
                nouveau_fbcon_remove(dev, drm_fb);
 
-       if (fb->nvbo) {
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(fb->nvbo->gem);
-               mutex_unlock(&dev->struct_mutex);
-       }
+       if (fb->nvbo)
+               drm_gem_object_unreference_unlocked(fb->nvbo->gem);
 
        drm_framebuffer_cleanup(drm_fb);
        kfree(fb);
index 50d9e67745af1e7d1c5872f39db736d2c76dfe05..c8482a108a7801c321875ecbf5ff4e041563d3e9 100644 (file)
 void
 nouveau_dma_pre_init(struct nouveau_channel *chan)
 {
-       chan->dma.max  = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
+       struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+       struct nouveau_bo *pushbuf = chan->pushbuf_bo;
+
+       if (dev_priv->card_type == NV_50) {
+               const int ib_size = pushbuf->bo.mem.size / 2;
+
+               chan->dma.ib_base = (pushbuf->bo.mem.size - ib_size) >> 2;
+               chan->dma.ib_max = (ib_size / 8) - 1;
+               chan->dma.ib_put = 0;
+               chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put;
+
+               chan->dma.max = (pushbuf->bo.mem.size - ib_size) >> 2;
+       } else {
+               chan->dma.max  = (pushbuf->bo.mem.size >> 2) - 2;
+       }
+
        chan->dma.put  = 0;
        chan->dma.cur  = chan->dma.put;
        chan->dma.free = chan->dma.max - chan->dma.cur;
@@ -162,12 +177,101 @@ READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)
        return (val - chan->pushbuf_base) >> 2;
 }
 
+void
+nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo,
+             int delta, int length)
+{
+       struct nouveau_bo *pb = chan->pushbuf_bo;
+       uint64_t offset = bo->bo.offset + delta;
+       int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base;
+
+       BUG_ON(chan->dma.ib_free < 1);
+       nouveau_bo_wr32(pb, ip++, lower_32_bits(offset));
+       nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8);
+
+       chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max;
+       nvchan_wr32(chan, 0x8c, chan->dma.ib_put);
+       chan->dma.ib_free--;
+}
+
+static int
+nv50_dma_push_wait(struct nouveau_channel *chan, int count)
+{
+       uint32_t cnt = 0, prev_get = 0;
+
+       while (chan->dma.ib_free < count) {
+               uint32_t get = nvchan_rd32(chan, 0x88);
+               if (get != prev_get) {
+                       prev_get = get;
+                       cnt = 0;
+               }
+
+               if ((++cnt & 0xff) == 0) {
+                       DRM_UDELAY(1);
+                       if (cnt > 100000)
+                               return -EBUSY;
+               }
+
+               chan->dma.ib_free = get - chan->dma.ib_put;
+               if (chan->dma.ib_free <= 0)
+                       chan->dma.ib_free += chan->dma.ib_max + 1;
+       }
+
+       return 0;
+}
+
+static int
+nv50_dma_wait(struct nouveau_channel *chan, int slots, int count)
+{
+       uint32_t cnt = 0, prev_get = 0;
+       int ret;
+
+       ret = nv50_dma_push_wait(chan, slots + 1);
+       if (unlikely(ret))
+               return ret;
+
+       while (chan->dma.free < count) {
+               int get = READ_GET(chan, &prev_get, &cnt);
+               if (unlikely(get < 0)) {
+                       if (get == -EINVAL)
+                               continue;
+
+                       return get;
+               }
+
+               if (get <= chan->dma.cur) {
+                       chan->dma.free = chan->dma.max - chan->dma.cur;
+                       if (chan->dma.free >= count)
+                               break;
+
+                       FIRE_RING(chan);
+                       do {
+                               get = READ_GET(chan, &prev_get, &cnt);
+                               if (unlikely(get < 0)) {
+                                       if (get == -EINVAL)
+                                               continue;
+                                       return get;
+                               }
+                       } while (get == 0);
+                       chan->dma.cur = 0;
+                       chan->dma.put = 0;
+               }
+
+               chan->dma.free = get - chan->dma.cur - 1;
+       }
+
+       return 0;
+}
+
 int
-nouveau_dma_wait(struct nouveau_channel *chan, int size)
+nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size)
 {
        uint32_t prev_get = 0, cnt = 0;
        int get;
 
+       if (chan->dma.ib_max)
+               return nv50_dma_wait(chan, slots, size);
+
        while (chan->dma.free < size) {
                get = READ_GET(chan, &prev_get, &cnt);
                if (unlikely(get == -EBUSY))
index dabfd655f93ec84e0a7f74a27f031c87c6234bfb..8b05c15866d5b9ad83bc52f78378944f90412fe9 100644 (file)
@@ -31,6 +31,9 @@
 #define NOUVEAU_DMA_DEBUG 0
 #endif
 
+void nv50_dma_push(struct nouveau_channel *, struct nouveau_bo *,
+                  int delta, int length);
+
 /*
  * There's a hw race condition where you can't jump to your PUT offset,
  * to avoid this we jump to offset + SKIPS and fill the difference with
@@ -96,13 +99,11 @@ enum {
 static __must_check inline int
 RING_SPACE(struct nouveau_channel *chan, int size)
 {
-       if (chan->dma.free < size) {
-               int ret;
+       int ret;
 
-               ret = nouveau_dma_wait(chan, size);
-               if (ret)
-                       return ret;
-       }
+       ret = nouveau_dma_wait(chan, 1, size);
+       if (ret)
+               return ret;
 
        chan->dma.free -= size;
        return 0;
@@ -146,7 +147,13 @@ FIRE_RING(struct nouveau_channel *chan)
                return;
        chan->accel_done = true;
 
-       WRITE_PUT(chan->dma.cur);
+       if (chan->dma.ib_max) {
+               nv50_dma_push(chan, chan->pushbuf_bo, chan->dma.put << 2,
+                             (chan->dma.cur - chan->dma.put) << 2);
+       } else {
+               WRITE_PUT(chan->dma.cur);
+       }
+
        chan->dma.put = chan->dma.cur;
 }
 
index da3b93b84502de4721f84193bbcbe1598c2b959b..30cc09e8a709383a458baab1d77ff89ca84de9da 100644 (file)
@@ -75,11 +75,11 @@ MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
 int nouveau_ignorelid = 0;
 module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
 
-MODULE_PARM_DESC(noagp, "Disable all acceleration");
+MODULE_PARM_DESC(noaccel, "Disable all acceleration");
 int nouveau_noaccel = 0;
 module_param_named(noaccel, nouveau_noaccel, int, 0400);
 
-MODULE_PARM_DESC(noagp, "Disable fbcon acceleration");
+MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
 int nouveau_nofbaccel = 0;
 module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
 
@@ -135,7 +135,7 @@ nouveau_pci_remove(struct pci_dev *pdev)
        drm_put_dev(dev);
 }
 
-static int
+int
 nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
@@ -233,7 +233,7 @@ out_abort:
        return ret;
 }
 
-static int
+int
 nouveau_pci_resume(struct pci_dev *pdev)
 {
        struct drm_device *dev = pci_get_drvdata(pdev);
@@ -402,8 +402,10 @@ static int __init nouveau_init(void)
                        nouveau_modeset = 1;
        }
 
-       if (nouveau_modeset == 1)
+       if (nouveau_modeset == 1) {
                driver.driver_features |= DRIVER_MODESET;
+               nouveau_register_dsm_handler();
+       }
 
        return drm_init(&driver);
 }
@@ -411,6 +413,7 @@ static int __init nouveau_init(void)
 static void __exit nouveau_exit(void)
 {
        drm_exit(&driver);
+       nouveau_unregister_dsm_handler();
 }
 
 module_init(nouveau_init);
index 1c15ef37b71cffc9c234f08b0337e50e1af4ff6e..5f8d987af3631a7fb06e612ae800bcff57de8bae 100644 (file)
@@ -34,7 +34,7 @@
 
 #define DRIVER_MAJOR           0
 #define DRIVER_MINOR           0
-#define DRIVER_PATCHLEVEL      15
+#define DRIVER_PATCHLEVEL      16
 
 #define NOUVEAU_FAMILY   0x0000FFFF
 #define NOUVEAU_FLAGS    0xFFFF0000
@@ -83,6 +83,7 @@ struct nouveau_bo {
        struct drm_file *reserved_by;
        struct list_head entry;
        int pbbo_index;
+       bool validate_mapped;
 
        struct nouveau_channel *channel;
 
@@ -239,6 +240,11 @@ struct nouveau_channel {
                int cur;
                int put;
                /* access via pushbuf_bo */
+
+               int ib_base;
+               int ib_max;
+               int ib_free;
+               int ib_put;
        } dma;
 
        uint32_t sw_subchannel[8];
@@ -533,6 +539,9 @@ struct drm_nouveau_private {
        struct nouveau_engine engine;
        struct nouveau_channel *channel;
 
+       /* For PFIFO and PGRAPH. */
+       spinlock_t context_switch_lock;
+
        /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
        struct nouveau_gpuobj *ramht;
        uint32_t ramin_rsvd_vram;
@@ -596,8 +605,7 @@ struct drm_nouveau_private {
 
        struct list_head gpuobj_list;
 
-       struct nvbios VBIOS;
-       struct nouveau_bios_info *vbios;
+       struct nvbios vbios;
 
        struct nv04_mode_state mode_reg;
        struct nv04_mode_state saved_reg;
@@ -614,7 +622,6 @@ struct drm_nouveau_private {
        } susres;
 
        struct backlight_device *backlight;
-       bool acpi_dsm;
 
        struct nouveau_channel *evo;
 
@@ -682,6 +689,9 @@ extern int nouveau_ignorelid;
 extern int nouveau_nofbaccel;
 extern int nouveau_noaccel;
 
+extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state);
+extern int nouveau_pci_resume(struct pci_dev *pdev);
+
 /* nouveau_state.c */
 extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
 extern int  nouveau_load(struct drm_device *, unsigned long flags);
@@ -696,12 +706,6 @@ extern bool nouveau_wait_until(struct drm_device *, uint64_t timeout,
                               uint32_t reg, uint32_t mask, uint32_t val);
 extern bool nouveau_wait_for_idle(struct drm_device *);
 extern int  nouveau_card_init(struct drm_device *);
-extern int  nouveau_ioctl_card_init(struct drm_device *, void *data,
-                                   struct drm_file *);
-extern int  nouveau_ioctl_suspend(struct drm_device *, void *data,
-                                 struct drm_file *);
-extern int  nouveau_ioctl_resume(struct drm_device *, void *data,
-                                struct drm_file *);
 
 /* nouveau_mem.c */
 extern int  nouveau_mem_init_heap(struct mem_block **, uint64_t start,
@@ -845,21 +849,15 @@ nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
 /* nouveau_dma.c */
 extern void nouveau_dma_pre_init(struct nouveau_channel *);
 extern int  nouveau_dma_init(struct nouveau_channel *);
-extern int  nouveau_dma_wait(struct nouveau_channel *, int size);
+extern int  nouveau_dma_wait(struct nouveau_channel *, int slots, int size);
 
 /* nouveau_acpi.c */
-#ifdef CONFIG_ACPI
-extern int nouveau_hybrid_setup(struct drm_device *dev);
-extern bool nouveau_dsm_probe(struct drm_device *dev);
+#if defined(CONFIG_ACPI)
+void nouveau_register_dsm_handler(void);
+void nouveau_unregister_dsm_handler(void);
 #else
-static inline int nouveau_hybrid_setup(struct drm_device *dev)
-{
-       return 0;
-}
-static inline bool nouveau_dsm_probe(struct drm_device *dev)
-{
-       return false;
-}
+static inline void nouveau_register_dsm_handler(void) {}
+static inline void nouveau_unregister_dsm_handler(void) {}
 #endif
 
 /* nouveau_backlight.c */
@@ -1027,6 +1025,7 @@ extern void nv50_graph_destroy_context(struct nouveau_channel *);
 extern int  nv50_graph_load_context(struct nouveau_channel *);
 extern int  nv50_graph_unload_context(struct drm_device *);
 extern void nv50_graph_context_switch(struct drm_device *);
+extern int  nv50_grctx_init(struct nouveau_grctx *);
 
 /* nouveau_grctx.c */
 extern int  nouveau_grctx_prog_load(struct drm_device *);
@@ -1152,16 +1151,6 @@ extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
                                 struct drm_file *);
 extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
                                     struct drm_file *);
-extern int nouveau_gem_ioctl_pushbuf_call(struct drm_device *, void *,
-                                         struct drm_file *);
-extern int nouveau_gem_ioctl_pushbuf_call2(struct drm_device *, void *,
-                                          struct drm_file *);
-extern int nouveau_gem_ioctl_pin(struct drm_device *, void *,
-                                struct drm_file *);
-extern int nouveau_gem_ioctl_unpin(struct drm_device *, void *,
-                                  struct drm_file *);
-extern int nouveau_gem_ioctl_tile(struct drm_device *, void *,
-                                 struct drm_file *);
 extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *,
                                      struct drm_file *);
 extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
index ea879a2efef328c8a452d8403bc93cfbd85cb879..68cedd9194fe0f3792c4152b1282b043300faf23 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/screen_info.h>
+#include <linux/vga_switcheroo.h>
 
 #include "drmP.h"
 #include "drm.h"
@@ -370,6 +371,7 @@ nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
                                                nvbo->bo.offset, nvbo);
 
        mutex_unlock(&dev->struct_mutex);
+       vga_switcheroo_client_fb_set(dev->pdev, info);
        return 0;
 
 out_unref:
@@ -401,10 +403,8 @@ nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
 
                unregister_framebuffer(info);
                nouveau_bo_unmap(nouveau_fb->nvbo);
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(nouveau_fb->nvbo->gem);
+               drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
                nouveau_fb->nvbo = NULL;
-               mutex_unlock(&dev->struct_mutex);
                if (par)
                        drm_fb_helper_free(&par->helper);
                framebuffer_release(info);
index 70cc30803e3bec1f28d4143faf1fc7754efb7986..0d22f66f1c795c1784cd3078c8e87f88dd4b8044 100644 (file)
@@ -167,12 +167,10 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
 
        ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
 out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_handle_unreference(nvbo->gem);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_handle_unreference_unlocked(nvbo->gem);
 
        if (ret)
-               drm_gem_object_unreference(nvbo->gem);
+               drm_gem_object_unreference_unlocked(nvbo->gem);
        return ret;
 }
 
@@ -243,6 +241,11 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
                        nouveau_fence_unref((void *)&prev_fence);
                }
 
+               if (unlikely(nvbo->validate_mapped)) {
+                       ttm_bo_kunmap(&nvbo->kmap);
+                       nvbo->validate_mapped = false;
+               }
+
                list_del(&nvbo->entry);
                nvbo->reserved_by = NULL;
                ttm_bo_unreserve(&nvbo->bo);
@@ -302,11 +305,14 @@ retry:
                        if (ret == -EAGAIN)
                                ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
                        drm_gem_object_unreference(gem);
-                       if (ret)
+                       if (ret) {
+                               NV_ERROR(dev, "fail reserve\n");
                                return ret;
+                       }
                        goto retry;
                }
 
+               b->user_priv = (uint64_t)(unsigned long)nvbo;
                nvbo->reserved_by = file_priv;
                nvbo->pbbo_index = i;
                if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
@@ -336,8 +342,10 @@ retry:
                        }
 
                        ret = ttm_bo_wait_cpu(&nvbo->bo, false);
-                       if (ret)
+                       if (ret) {
+                               NV_ERROR(dev, "fail wait_cpu\n");
                                return ret;
+                       }
                        goto retry;
                }
        }
@@ -351,6 +359,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
 {
        struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
                                (void __force __user *)(uintptr_t)user_pbbo_ptr;
+       struct drm_device *dev = chan->dev;
        struct nouveau_bo *nvbo;
        int ret, relocs = 0;
 
@@ -362,39 +371,46 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
                        spin_lock(&nvbo->bo.lock);
                        ret = ttm_bo_wait(&nvbo->bo, false, false, false);
                        spin_unlock(&nvbo->bo.lock);
-                       if (unlikely(ret))
+                       if (unlikely(ret)) {
+                               NV_ERROR(dev, "fail wait other chan\n");
                                return ret;
+                       }
                }
 
                ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
                                             b->write_domains,
                                             b->valid_domains);
-               if (unlikely(ret))
+               if (unlikely(ret)) {
+                       NV_ERROR(dev, "fail set_domain\n");
                        return ret;
+               }
 
                nvbo->channel = chan;
                ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement,
                                      false, false);
                nvbo->channel = NULL;
-               if (unlikely(ret))
+               if (unlikely(ret)) {
+                       NV_ERROR(dev, "fail ttm_validate\n");
                        return ret;
+               }
 
-               if (nvbo->bo.offset == b->presumed_offset &&
+               if (nvbo->bo.offset == b->presumed.offset &&
                    ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
-                     b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
+                     b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
                     (nvbo->bo.mem.mem_type == TTM_PL_TT &&
-                     b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART)))
+                     b->presumed.domain & NOUVEAU_GEM_DOMAIN_GART)))
                        continue;
 
                if (nvbo->bo.mem.mem_type == TTM_PL_TT)
-                       b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART;
+                       b->presumed.domain = NOUVEAU_GEM_DOMAIN_GART;
                else
-                       b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM;
-               b->presumed_offset = nvbo->bo.offset;
-               b->presumed_ok = 0;
+                       b->presumed.domain = NOUVEAU_GEM_DOMAIN_VRAM;
+               b->presumed.offset = nvbo->bo.offset;
+               b->presumed.valid = 0;
                relocs++;
 
-               if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b)))
+               if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index].presumed,
+                                    &b->presumed, sizeof(b->presumed)))
                        return -EFAULT;
        }
 
@@ -408,6 +424,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
                             uint64_t user_buffers, int nr_buffers,
                             struct validate_op *op, int *apply_relocs)
 {
+       struct drm_device *dev = chan->dev;
        int ret, relocs = 0;
 
        INIT_LIST_HEAD(&op->vram_list);
@@ -418,11 +435,14 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
                return 0;
 
        ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
-       if (unlikely(ret))
+       if (unlikely(ret)) {
+               NV_ERROR(dev, "validate_init\n");
                return ret;
+       }
 
        ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
+               NV_ERROR(dev, "validate vram_list\n");
                validate_fini(op, NULL);
                return ret;
        }
@@ -430,6 +450,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
 
        ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
+               NV_ERROR(dev, "validate gart_list\n");
                validate_fini(op, NULL);
                return ret;
        }
@@ -437,6 +458,7 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
 
        ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
        if (unlikely(ret < 0)) {
+               NV_ERROR(dev, "validate both_list\n");
                validate_fini(op, NULL);
                return ret;
        }
@@ -465,59 +487,82 @@ u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
 }
 
 static int
-nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo,
-                               struct drm_nouveau_gem_pushbuf_bo *bo,
-                               unsigned nr_relocs, uint64_t ptr_relocs,
-                               unsigned nr_dwords, unsigned first_dword,
-                               uint32_t *pushbuf, bool is_iomem)
+nouveau_gem_pushbuf_reloc_apply(struct drm_device *dev,
+                               struct drm_nouveau_gem_pushbuf *req,
+                               struct drm_nouveau_gem_pushbuf_bo *bo)
 {
        struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
-       struct drm_device *dev = chan->dev;
        int ret = 0;
        unsigned i;
 
-       reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc));
+       reloc = u_memcpya(req->relocs, req->nr_relocs, sizeof(*reloc));
        if (IS_ERR(reloc))
                return PTR_ERR(reloc);
 
-       for (i = 0; i < nr_relocs; i++) {
+       for (i = 0; i < req->nr_relocs; i++) {
                struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i];
                struct drm_nouveau_gem_pushbuf_bo *b;
+               struct nouveau_bo *nvbo;
                uint32_t data;
 
-               if (r->bo_index >= nr_bo || r->reloc_index < first_dword ||
-                   r->reloc_index >= first_dword + nr_dwords) {
-                       NV_ERROR(dev, "Bad relocation %d\n", i);
-                       NV_ERROR(dev, "  bo: %d max %d\n", r->bo_index, nr_bo);
-                       NV_ERROR(dev, "  id: %d max %d\n", r->reloc_index, nr_dwords);
+               if (unlikely(r->bo_index > req->nr_buffers)) {
+                       NV_ERROR(dev, "reloc bo index invalid\n");
                        ret = -EINVAL;
                        break;
                }
 
                b = &bo[r->bo_index];
-               if (b->presumed_ok)
+               if (b->presumed.valid)
                        continue;
 
+               if (unlikely(r->reloc_bo_index > req->nr_buffers)) {
+                       NV_ERROR(dev, "reloc container bo index invalid\n");
+                       ret = -EINVAL;
+                       break;
+               }
+               nvbo = (void *)(unsigned long)bo[r->reloc_bo_index].user_priv;
+
+               if (unlikely(r->reloc_bo_offset + 4 >
+                            nvbo->bo.mem.num_pages << PAGE_SHIFT)) {
+                       NV_ERROR(dev, "reloc outside of bo\n");
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (!nvbo->kmap.virtual) {
+                       ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages,
+                                         &nvbo->kmap);
+                       if (ret) {
+                               NV_ERROR(dev, "failed kmap for reloc\n");
+                               break;
+                       }
+                       nvbo->validate_mapped = true;
+               }
+
                if (r->flags & NOUVEAU_GEM_RELOC_LOW)
-                       data = b->presumed_offset + r->data;
+                       data = b->presumed.offset + r->data;
                else
                if (r->flags & NOUVEAU_GEM_RELOC_HIGH)
-                       data = (b->presumed_offset + r->data) >> 32;
+                       data = (b->presumed.offset + r->data) >> 32;
                else
                        data = r->data;
 
                if (r->flags & NOUVEAU_GEM_RELOC_OR) {
-                       if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART)
+                       if (b->presumed.domain == NOUVEAU_GEM_DOMAIN_GART)
                                data |= r->tor;
                        else
                                data |= r->vor;
                }
 
-               if (is_iomem)
-                       iowrite32_native(data, (void __force __iomem *)
-                                               &pushbuf[r->reloc_index]);
-               else
-                       pushbuf[r->reloc_index] = data;
+               spin_lock(&nvbo->bo.lock);
+               ret = ttm_bo_wait(&nvbo->bo, false, false, false);
+               spin_unlock(&nvbo->bo.lock);
+               if (ret) {
+                       NV_ERROR(dev, "reloc wait_idle failed: %d\n", ret);
+                       break;
+               }
+
+               nouveau_bo_wr32(nvbo, r->reloc_bo_offset >> 2, data);
        }
 
        kfree(reloc);
@@ -528,127 +573,50 @@ int
 nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
                          struct drm_file *file_priv)
 {
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct drm_nouveau_gem_pushbuf *req = data;
-       struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
+       struct drm_nouveau_gem_pushbuf_push *push;
+       struct drm_nouveau_gem_pushbuf_bo *bo;
        struct nouveau_channel *chan;
        struct validate_op op;
-       struct nouveau_fence* fence = 0;
-       uint32_t *pushbuf = NULL;
-       int ret = 0, do_reloc = 0, i;
+       struct nouveau_fence *fence = 0;
+       int i, j, ret = 0, do_reloc = 0;
 
        NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
        NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
 
-       if (req->nr_dwords >= chan->dma.max ||
-           req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
-           req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
-               NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
-               NV_ERROR(dev, "  dwords : %d max %d\n", req->nr_dwords,
-                        chan->dma.max - 1);
-               NV_ERROR(dev, "  buffers: %d max %d\n", req->nr_buffers,
-                        NOUVEAU_GEM_MAX_BUFFERS);
-               NV_ERROR(dev, "  relocs : %d max %d\n", req->nr_relocs,
-                        NOUVEAU_GEM_MAX_RELOCS);
-               return -EINVAL;
-       }
-
-       pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t));
-       if (IS_ERR(pushbuf))
-               return PTR_ERR(pushbuf);
-
-       bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
-       if (IS_ERR(bo)) {
-               kfree(pushbuf);
-               return PTR_ERR(bo);
-       }
-
-       mutex_lock(&dev->struct_mutex);
-
-       /* Validate buffer list */
-       ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
-                                          req->nr_buffers, &op, &do_reloc);
-       if (ret)
-               goto out;
-
-       /* Apply any relocations that are required */
-       if (do_reloc) {
-               ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers,
-                                                     bo, req->nr_relocs,
-                                                     req->relocs,
-                                                     req->nr_dwords, 0,
-                                                     pushbuf, false);
-               if (ret)
-                       goto out;
-       }
-
-       /* Emit push buffer to the hw
-        */
-       ret = RING_SPACE(chan, req->nr_dwords);
-       if (ret)
-               goto out;
-
-       OUT_RINGp(chan, pushbuf, req->nr_dwords);
+       req->vram_available = dev_priv->fb_aper_free;
+       req->gart_available = dev_priv->gart_info.aper_free;
+       if (unlikely(req->nr_push == 0))
+               goto out_next;
 
-       ret = nouveau_fence_new(chan, &fence, true);
-       if (ret) {
-               NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
-               WIND_RING(chan);
-               goto out;
+       if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) {
+               NV_ERROR(dev, "pushbuf push count exceeds limit: %d max %d\n",
+                        req->nr_push, NOUVEAU_GEM_MAX_PUSH);
+               return -EINVAL;
        }
 
-       if (nouveau_gem_pushbuf_sync(chan)) {
-               ret = nouveau_fence_wait(fence, NULL, false, false);
-               if (ret) {
-                       for (i = 0; i < req->nr_dwords; i++)
-                               NV_ERROR(dev, "0x%08x\n", pushbuf[i]);
-                       NV_ERROR(dev, "^^ above push buffer is fail :(\n");
-               }
+       if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) {
+               NV_ERROR(dev, "pushbuf bo count exceeds limit: %d max %d\n",
+                        req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS);
+               return -EINVAL;
        }
 
-out:
-       validate_fini(&op, fence);
-       nouveau_fence_unref((void**)&fence);
-       mutex_unlock(&dev->struct_mutex);
-       kfree(pushbuf);
-       kfree(bo);
-       return ret;
-}
-
-#define PUSHBUF_CAL (dev_priv->card_type >= NV_20)
-
-int
-nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data,
-                              struct drm_file *file_priv)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct drm_nouveau_gem_pushbuf_call *req = data;
-       struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
-       struct nouveau_channel *chan;
-       struct drm_gem_object *gem;
-       struct nouveau_bo *pbbo;
-       struct validate_op op;
-       struct nouveau_fence* fence = 0;
-       int i, ret = 0, do_reloc = 0;
-
-       NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
-       NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
-
-       if (unlikely(req->handle == 0))
-               goto out_next;
-
-       if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
-           req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
-               NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
-               NV_ERROR(dev, "  buffers: %d max %d\n", req->nr_buffers,
-                        NOUVEAU_GEM_MAX_BUFFERS);
-               NV_ERROR(dev, "  relocs : %d max %d\n", req->nr_relocs,
-                        NOUVEAU_GEM_MAX_RELOCS);
+       if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) {
+               NV_ERROR(dev, "pushbuf reloc count exceeds limit: %d max %d\n",
+                        req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS);
                return -EINVAL;
        }
 
+       push = u_memcpya(req->push, req->nr_push, sizeof(*push));
+       if (IS_ERR(push))
+               return PTR_ERR(push);
+
        bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
-       if (IS_ERR(bo))
+       if (IS_ERR(bo)) {
+               kfree(push);
                return PTR_ERR(bo);
+       }
 
        mutex_lock(&dev->struct_mutex);
 
@@ -660,122 +628,84 @@ nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data,
                goto out;
        }
 
-       /* Validate DMA push buffer */
-       gem = drm_gem_object_lookup(dev, file_priv, req->handle);
-       if (!gem) {
-               NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle);
-               ret = -EINVAL;
-               goto out;
-       }
-       pbbo = nouveau_gem_object(gem);
-
-       if ((req->offset & 3) || req->nr_dwords < 2 ||
-           (unsigned long)req->offset > (unsigned long)pbbo->bo.mem.size ||
-           (unsigned long)req->nr_dwords >
-            ((unsigned long)(pbbo->bo.mem.size - req->offset ) >> 2)) {
-               NV_ERROR(dev, "pb call misaligned or out of bounds: "
-                             "%d + %d * 4 > %ld\n",
-                        req->offset, req->nr_dwords, pbbo->bo.mem.size);
-               ret = -EINVAL;
-               drm_gem_object_unreference(gem);
-               goto out;
-       }
-
-       ret = ttm_bo_reserve(&pbbo->bo, false, false, true,
-                            chan->fence.sequence);
-       if (ret) {
-               NV_ERROR(dev, "resv pb: %d\n", ret);
-               drm_gem_object_unreference(gem);
-               goto out;
-       }
-
-       nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type);
-       ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false);
-       if (ret) {
-               NV_ERROR(dev, "validate pb: %d\n", ret);
-               ttm_bo_unreserve(&pbbo->bo);
-               drm_gem_object_unreference(gem);
-               goto out;
-       }
-
-       list_add_tail(&pbbo->entry, &op.both_list);
-
-       /* If presumed return address doesn't match, we need to map the
-        * push buffer and fix it..
-        */
-       if (!PUSHBUF_CAL) {
-               uint32_t retaddy;
-
-               if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) {
-                       ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS);
-                       if (ret) {
-                               NV_ERROR(dev, "jmp_space: %d\n", ret);
-                               goto out;
-                       }
-               }
-
-               retaddy  = chan->pushbuf_base + ((chan->dma.cur + 2) << 2);
-               retaddy |= 0x20000000;
-               if (retaddy != req->suffix0) {
-                       req->suffix0 = retaddy;
-                       do_reloc = 1;
-               }
-       }
-
        /* Apply any relocations that are required */
        if (do_reloc) {
-               void *pbvirt;
-               bool is_iomem;
-               ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages,
-                                 &pbbo->kmap);
+               ret = nouveau_gem_pushbuf_reloc_apply(dev, req, bo);
                if (ret) {
-                       NV_ERROR(dev, "kmap pb: %d\n", ret);
+                       NV_ERROR(dev, "reloc apply: %d\n", ret);
                        goto out;
                }
+       }
 
-               pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem);
-               ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo,
-                                                     req->nr_relocs,
-                                                     req->relocs,
-                                                     req->nr_dwords,
-                                                     req->offset / 4,
-                                                     pbvirt, is_iomem);
-
-               if (!PUSHBUF_CAL) {
-                       nouveau_bo_wr32(pbbo,
-                                       req->offset / 4 + req->nr_dwords - 2,
-                                       req->suffix0);
-               }
-
-               ttm_bo_kunmap(&pbbo->kmap);
+       if (chan->dma.ib_max) {
+               ret = nouveau_dma_wait(chan, req->nr_push + 1, 6);
                if (ret) {
-                       NV_ERROR(dev, "reloc apply: %d\n", ret);
+                       NV_INFO(dev, "nv50cal_space: %d\n", ret);
                        goto out;
                }
-       }
 
-       if (PUSHBUF_CAL) {
-               ret = RING_SPACE(chan, 2);
+               for (i = 0; i < req->nr_push; i++) {
+                       struct nouveau_bo *nvbo = (void *)(unsigned long)
+                               bo[push[i].bo_index].user_priv;
+
+                       nv50_dma_push(chan, nvbo, push[i].offset,
+                                     push[i].length);
+               }
+       } else
+       if (dev_priv->card_type >= NV_20) {
+               ret = RING_SPACE(chan, req->nr_push * 2);
                if (ret) {
                        NV_ERROR(dev, "cal_space: %d\n", ret);
                        goto out;
                }
-               OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
-                                 req->offset) | 2);
-               OUT_RING(chan, 0);
+
+               for (i = 0; i < req->nr_push; i++) {
+                       struct nouveau_bo *nvbo = (void *)(unsigned long)
+                               bo[push[i].bo_index].user_priv;
+                       struct drm_mm_node *mem = nvbo->bo.mem.mm_node;
+
+                       OUT_RING(chan, ((mem->start << PAGE_SHIFT) +
+                                       push[i].offset) | 2);
+                       OUT_RING(chan, 0);
+               }
        } else {
-               ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS);
+               ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS));
                if (ret) {
                        NV_ERROR(dev, "jmp_space: %d\n", ret);
                        goto out;
                }
-               OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
-                                 req->offset) | 0x20000000);
-               OUT_RING(chan, 0);
 
-               /* Space the jumps apart with NOPs. */
-               for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
+               for (i = 0; i < req->nr_push; i++) {
+                       struct nouveau_bo *nvbo = (void *)(unsigned long)
+                               bo[push[i].bo_index].user_priv;
+                       struct drm_mm_node *mem = nvbo->bo.mem.mm_node;
+                       uint32_t cmd;
+
+                       cmd = chan->pushbuf_base + ((chan->dma.cur + 2) << 2);
+                       cmd |= 0x20000000;
+                       if (unlikely(cmd != req->suffix0)) {
+                               if (!nvbo->kmap.virtual) {
+                                       ret = ttm_bo_kmap(&nvbo->bo, 0,
+                                                         nvbo->bo.mem.
+                                                         num_pages,
+                                                         &nvbo->kmap);
+                                       if (ret) {
+                                               WIND_RING(chan);
+                                               goto out;
+                                       }
+                                       nvbo->validate_mapped = true;
+                               }
+
+                               nouveau_bo_wr32(nvbo, (push[i].offset +
+                                               push[i].length - 8) / 4, cmd);
+                       }
+
+                       OUT_RING(chan, ((mem->start << PAGE_SHIFT) +
+                                       push[i].offset) | 0x20000000);
                        OUT_RING(chan, 0);
+                       for (j = 0; j < NOUVEAU_DMA_SKIPS; j++)
+                               OUT_RING(chan, 0);
+               }
        }
 
        ret = nouveau_fence_new(chan, &fence, true);
@@ -790,9 +720,14 @@ out:
        nouveau_fence_unref((void**)&fence);
        mutex_unlock(&dev->struct_mutex);
        kfree(bo);
+       kfree(push);
 
 out_next:
-       if (PUSHBUF_CAL) {
+       if (chan->dma.ib_max) {
+               req->suffix0 = 0x00000000;
+               req->suffix1 = 0x00000000;
+       } else
+       if (dev_priv->card_type >= NV_20) {
                req->suffix0 = 0x00020000;
                req->suffix1 = 0x00000000;
        } else {
@@ -804,19 +739,6 @@ out_next:
        return ret;
 }
 
-int
-nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data,
-                               struct drm_file *file_priv)
-{
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct drm_nouveau_gem_pushbuf_call *req = data;
-
-       req->vram_available = dev_priv->fb_aper_free;
-       req->gart_available = dev_priv->gart_info.aper_free;
-
-       return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv);
-}
-
 static inline uint32_t
 domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
 {
@@ -830,74 +752,6 @@ domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
        return flags;
 }
 
-int
-nouveau_gem_ioctl_pin(struct drm_device *dev, void *data,
-                     struct drm_file *file_priv)
-{
-       struct drm_nouveau_gem_pin *req = data;
-       struct drm_gem_object *gem;
-       struct nouveau_bo *nvbo;
-       int ret = 0;
-
-       NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               NV_ERROR(dev, "pin only allowed without kernel modesetting\n");
-               return -EINVAL;
-       }
-
-       if (!DRM_SUSER(DRM_CURPROC))
-               return -EPERM;
-
-       gem = drm_gem_object_lookup(dev, file_priv, req->handle);
-       if (!gem)
-               return -EINVAL;
-       nvbo = nouveau_gem_object(gem);
-
-       ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain));
-       if (ret)
-               goto out;
-
-       req->offset = nvbo->bo.offset;
-       if (nvbo->bo.mem.mem_type == TTM_PL_TT)
-               req->domain = NOUVEAU_GEM_DOMAIN_GART;
-       else
-               req->domain = NOUVEAU_GEM_DOMAIN_VRAM;
-
-out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
-
-       return ret;
-}
-
-int
-nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
-{
-       struct drm_nouveau_gem_pin *req = data;
-       struct drm_gem_object *gem;
-       int ret;
-
-       NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
-
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       gem = drm_gem_object_lookup(dev, file_priv, req->handle);
-       if (!gem)
-               return -EINVAL;
-
-       ret = nouveau_bo_unpin(nouveau_gem_object(gem));
-
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
-
-       return ret;
-}
-
 int
 nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
                           struct drm_file *file_priv)
@@ -935,9 +789,7 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
        }
 
 out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gem);
        return ret;
 }
 
@@ -965,9 +817,7 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
        ret = 0;
 
 out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gem);
        return ret;
 }
 
@@ -986,9 +836,7 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
                return -EINVAL;
 
        ret = nouveau_gem_info(gem, req);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gem);
        return ret;
 }
 
index dc46792a5c96b9f10daf90d4ef83b17a5fdc3f2e..7855b35effc357e2a6db34db2f181a9ea5663f0f 100644 (file)
@@ -160,7 +160,7 @@ static void
 setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int chip_version = dev_priv->vbios->chip_version;
+       int chip_version = dev_priv->vbios.chip_version;
        uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
        int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
        uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
@@ -216,7 +216,7 @@ setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
                       struct nouveau_pll_vals *pv)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int chip_version = dev_priv->vbios->chip_version;
+       int chip_version = dev_priv->vbios.chip_version;
        bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
        uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
        uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
@@ -374,7 +374,7 @@ nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
                  struct nouveau_pll_vals *pv)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       int cv = dev_priv->vbios->chip_version;
+       int cv = dev_priv->vbios.chip_version;
 
        if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
            cv >= 0x40) {
index 70e994d281223ef9c69a563d14959c8959851bad..88583e7bf651383237187d73305222351ae7a26c 100644 (file)
@@ -254,16 +254,16 @@ struct nouveau_i2c_chan *
 nouveau_i2c_find(struct drm_device *dev, int index)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
 
-       if (index > DCB_MAX_NUM_I2C_ENTRIES)
+       if (index >= DCB_MAX_NUM_I2C_ENTRIES)
                return NULL;
 
-       if (!bios->bdcb.dcb.i2c[index].chan) {
-               if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index))
+       if (!bios->dcb.i2c[index].chan) {
+               if (nouveau_i2c_init(dev, &bios->dcb.i2c[index], index))
                        return NULL;
        }
 
-       return bios->bdcb.dcb.i2c[index].chan;
+       return bios->dcb.i2c[index].chan;
 }
 
index 447f9f69d6b14fd28b5d01de67f0771c9aefa913..95220ddebb45a0f1880e3b200e161480588a9133 100644 (file)
@@ -691,11 +691,14 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
        struct drm_device *dev = (struct drm_device *)arg;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t status, fbdev_flags = 0;
+       unsigned long flags;
 
        status = nv_rd32(dev, NV03_PMC_INTR_0);
        if (!status)
                return IRQ_NONE;
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        if (dev_priv->fbdev_info) {
                fbdev_flags = dev_priv->fbdev_info->flags;
                dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
@@ -733,5 +736,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
        if (dev_priv->fbdev_info)
                dev_priv->fbdev_info->flags = fbdev_flags;
 
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
        return IRQ_HANDLED;
 }
index d99dc087f9b1b774316b9b4e41407c9fa4c4ffa5..9537f3e30115fb16135794aa674fa2a65da7ecb7 100644 (file)
@@ -61,11 +61,8 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan)
 
        chan->notifier_bo = ntfy;
 out_err:
-       if (ret) {
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(ntfy->gem);
-               mutex_unlock(&dev->struct_mutex);
-       }
+       if (ret)
+               drm_gem_object_unreference_unlocked(ntfy->gem);
 
        return ret;
 }
@@ -81,8 +78,8 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
        nouveau_bo_unmap(chan->notifier_bo);
        mutex_lock(&dev->struct_mutex);
        nouveau_bo_unpin(chan->notifier_bo);
-       drm_gem_object_unreference(chan->notifier_bo->gem);
        mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(chan->notifier_bo->gem);
        nouveau_mem_takedown(&chan->notifier_heap);
 }
 
index a4851af5b05ec535816eeb98973675c6cbb03aa4..eb8f084d5f537ec4edeb2444b992763e15549ade 100644 (file)
@@ -29,6 +29,7 @@
 #include "drm_sarea.h"
 #include "drm_crtc_helper.h"
 #include <linux/vgaarb.h>
+#include <linux/vga_switcheroo.h>
 
 #include "nouveau_drv.h"
 #include "nouveau_drm.h"
@@ -371,6 +372,30 @@ out_err:
        return ret;
 }
 
+static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
+                                        enum vga_switcheroo_state state)
+{
+       pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+       if (state == VGA_SWITCHEROO_ON) {
+               printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
+               nouveau_pci_resume(pdev);
+       } else {
+               printk(KERN_ERR "VGA switcheroo: switched nouveau off\n");
+               nouveau_pci_suspend(pdev, pmm);
+       }
+}
+
+static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
+}
+
 int
 nouveau_card_init(struct drm_device *dev)
 {
@@ -384,6 +409,8 @@ nouveau_card_init(struct drm_device *dev)
                return 0;
 
        vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
+       vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state,
+                                      nouveau_switcheroo_can_switch);
 
        /* Initialise internal driver API hooks */
        ret = nouveau_init_engine_ptrs(dev);
@@ -391,6 +418,7 @@ nouveau_card_init(struct drm_device *dev)
                goto out;
        engine = &dev_priv->engine;
        dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
+       spin_lock_init(&dev_priv->context_switch_lock);
 
        /* Parse BIOS tables / Run init tables if card not POSTed */
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
@@ -617,11 +645,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
        NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
                 dev->pci_vendor, dev->pci_device, dev->pdev->class);
 
-       dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
-
-       if (dev_priv->acpi_dsm)
-               nouveau_hybrid_setup(dev);
-
        dev_priv->wq = create_workqueue("nouveau");
        if (!dev_priv->wq)
                return -EINVAL;
@@ -776,13 +799,6 @@ int nouveau_unload(struct drm_device *dev)
        return 0;
 }
 
-int
-nouveau_ioctl_card_init(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
-{
-       return nouveau_card_init(dev);
-}
-
 int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
                                                struct drm_file *file_priv)
 {
index d2f143ed97c1000e0f3fa925eebb9cbcf3c3f29a..a1d1ebb073d930e64342847ebd0a6add0ec0bc03 100644 (file)
@@ -926,9 +926,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
        nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
        nv_crtc->cursor.show(nv_crtc, true);
 out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gem);
        return ret;
 }
 
index 1d73b15d70daf52c52e83dcac5a9200f304c288a..1cb19e3acb554a998765c98a4aa31c708cd36caf 100644 (file)
@@ -230,13 +230,13 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
        if (dcb->type == OUTPUT_TV) {
                testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
 
-               if (dev_priv->vbios->tvdactestval)
-                       testval = dev_priv->vbios->tvdactestval;
+               if (dev_priv->vbios.tvdactestval)
+                       testval = dev_priv->vbios.tvdactestval;
        } else {
                testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
 
-               if (dev_priv->vbios->dactestval)
-                       testval = dev_priv->vbios->dactestval;
+               if (dev_priv->vbios.dactestval)
+                       testval = dev_priv->vbios.dactestval;
        }
 
        saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
index 483f875bdb6a479f04b5387b9d20d0e713915c74..41634d4752fe08f6309d81c0b81ff0b7a65cc64f 100644 (file)
@@ -269,10 +269,10 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
        regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
        if (!nv_gf4_disp_arch(dev) ||
            (output_mode->hsync_start - output_mode->hdisplay) >=
-                                       dev_priv->vbios->digital_min_front_porch)
+                                       dev_priv->vbios.digital_min_front_porch)
                regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
        else
-               regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
+               regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios.digital_min_front_porch - 1;
        regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
        regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
        regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
index ef77215fa5b98372f780911d2e8006fce458f471..c7898b4f6dfbe0e1fb440452368d22077a0b083d 100644 (file)
@@ -93,10 +93,9 @@ int
 nv04_display_create(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct parsed_dcb *dcb = dev_priv->vbios->dcb;
+       struct dcb_table *dcb = &dev_priv->vbios.dcb;
        struct drm_encoder *encoder;
        struct drm_crtc *crtc;
-       uint16_t connector[16] = { 0 };
        int i, ret;
 
        NV_DEBUG_KMS(dev, "\n");
@@ -154,52 +153,10 @@ nv04_display_create(struct drm_device *dev)
 
                if (ret)
                        continue;
-
-               connector[dcbent->connector] |= (1 << dcbent->type);
        }
 
-       for (i = 0; i < dcb->entries; i++) {
-               struct dcb_entry *dcbent = &dcb->entry[i];
-               uint16_t encoders;
-               int type;
-
-               encoders = connector[dcbent->connector];
-               if (!(encoders & (1 << dcbent->type)))
-                       continue;
-               connector[dcbent->connector] = 0;
-
-               switch (dcbent->type) {
-               case OUTPUT_ANALOG:
-                       if (!MULTIPLE_ENCODERS(encoders))
-                               type = DRM_MODE_CONNECTOR_VGA;
-                       else
-                               type = DRM_MODE_CONNECTOR_DVII;
-                       break;
-               case OUTPUT_TMDS:
-                       if (!MULTIPLE_ENCODERS(encoders))
-                               type = DRM_MODE_CONNECTOR_DVID;
-                       else
-                               type = DRM_MODE_CONNECTOR_DVII;
-                       break;
-               case OUTPUT_LVDS:
-                       type = DRM_MODE_CONNECTOR_LVDS;
-#if 0
-                       /* don't create i2c adapter when lvds ddc not allowed */
-                       if (dcbent->lvdsconf.use_straps_for_mode ||
-                           dev_priv->vbios->fp_no_ddc)
-                               i2c_index = 0xf;
-#endif
-                       break;
-               case OUTPUT_TV:
-                       type = DRM_MODE_CONNECTOR_TV;
-                       break;
-               default:
-                       type = DRM_MODE_CONNECTOR_Unknown;
-                       continue;
-               }
-
-               nouveau_connector_create(dev, dcbent->connector, type);
-       }
+       for (i = 0; i < dcb->connector.entries; i++)
+               nouveau_connector_create(dev, &dcb->connector.entry[i]);
 
        /* Save previous state */
        NVLockVgaCrtcs(dev, false);
index fd01caabd5c3aee9efbc93bda38b3707adb14a84..3da90c2c4e634910eba368bdb691bf9684cd5b95 100644 (file)
@@ -118,7 +118,7 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
                return;
        }
 
-       width = (image->width + 31) & ~31;
+       width = ALIGN(image->width, 32);
        dsize = (width * image->height) >> 5;
 
        if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
index f31347b8c9b05da1977fcd00b5108c725434f689..66fe55983b6e3b7b9cf7b14700a7c1ba5b5681c9 100644 (file)
@@ -117,6 +117,7 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
 {
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
+       unsigned long flags;
        int ret;
 
        ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
@@ -127,6 +128,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
        if (ret)
                return ret;
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        /* Setup initial state */
        dev_priv->engine.instmem.prepare_access(dev, true);
        RAMFC_WR(DMA_PUT, chan->pushbuf_base);
@@ -144,6 +147,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
        /* enable the fifo dma operation */
        nv_wr32(dev, NV04_PFIFO_MODE,
                nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
        return 0;
 }
 
index 9c63099e9c428a74f04f23095950d181c631c142..c4e3404337d4708e7b60c57e538908455f2ac235 100644 (file)
@@ -262,7 +262,7 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
        nv_encoder->or = ffs(entry->or) - 1;
 
        /* Run the slave-specific initialization */
-       adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
+       adap = &dev_priv->vbios.dcb.i2c[i2c_index].chan->adapter;
 
        was_locked = NVLockVgaCrtcs(dev, false);
 
index 21ac6e49b6ee52bd4891f7710e94d9356595c546..74c880374fb92a5593583b4d077bac002ec76c3c 100644 (file)
@@ -45,8 +45,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
 
 #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
        testval = RGB_TEST_DATA(0x82, 0xeb, 0x82);
-       if (dev_priv->vbios->tvdactestval)
-               testval = dev_priv->vbios->tvdactestval;
+       if (dev_priv->vbios.tvdactestval)
+               testval = dev_priv->vbios.tvdactestval;
 
        dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
        head = (dacclk & 0x100) >> 8;
@@ -367,7 +367,7 @@ static void nv17_tv_prepare(struct drm_encoder *encoder)
                             !enc->crtc &&
                             nv04_dfp_get_bound_head(dev, dcb) == head) {
                                nv04_dfp_bind_head(dev, dcb, head ^ 1,
-                                               dev_priv->VBIOS.fp.dual_link);
+                                               dev_priv->vbios.fp.dual_link);
                        }
                }
 
index b4f19ccb8b41a2c9f96f460b734aa2971bdd9ca8..6b2ef4a9fce17c135c17ebe410866ef8f60d4b30 100644 (file)
@@ -37,6 +37,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t fc = NV40_RAMFC(chan->id);
+       unsigned long flags;
        int ret;
 
        ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
@@ -45,6 +46,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
        if (ret)
                return ret;
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        dev_priv->engine.instmem.prepare_access(dev, true);
        nv_wi32(dev, fc +  0, chan->pushbuf_base);
        nv_wi32(dev, fc +  4, chan->pushbuf_base);
@@ -63,6 +66,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
        /* enable the fifo dma operation */
        nv_wr32(dev, NV04_PFIFO_MODE,
                nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
        return 0;
 }
 
index d1a651e3400cf5273e79dd210486fb96c8e79664..cfabeb974a56c4cf1756981133030645b84481e6 100644 (file)
@@ -358,9 +358,7 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
        nv_crtc->cursor.show(nv_crtc, true);
 
 out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gem);
        return ret;
 }
 
index f08f042a8e10de23a06c14615bac5bcaad4e6af5..1fd9537beff60c404ebded245764edb3e1845802 100644 (file)
@@ -79,8 +79,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
        }
 
        /* Use bios provided value if possible. */
-       if (dev_priv->vbios->dactestval) {
-               load_pattern = dev_priv->vbios->dactestval;
+       if (dev_priv->vbios.dactestval) {
+               load_pattern = dev_priv->vbios.dactestval;
                NV_DEBUG_KMS(dev, "Using bios provided load_pattern of %d\n",
                          load_pattern);
        } else {
index 90f0bf59fbcd191dabcb5803f50537938a481405..61a89f2dc5535ecf1d74f08b9c342727997c1766 100644 (file)
@@ -370,9 +370,7 @@ nv50_display_init(struct drm_device *dev)
                struct nouveau_connector *conn = nouveau_connector(connector);
                struct dcb_gpio_entry *gpio;
 
-               if (connector->connector_type != DRM_MODE_CONNECTOR_DVII &&
-                   connector->connector_type != DRM_MODE_CONNECTOR_DVID &&
-                   connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+               if (conn->dcb->gpio_tag == 0xff)
                        continue;
 
                gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag);
@@ -465,8 +463,7 @@ static int nv50_display_disable(struct drm_device *dev)
 int nv50_display_create(struct drm_device *dev)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct parsed_dcb *dcb = dev_priv->vbios->dcb;
-       uint32_t connector[16] = {};
+       struct dcb_table *dcb = &dev_priv->vbios.dcb;
        int ret, i;
 
        NV_DEBUG_KMS(dev, "\n");
@@ -522,44 +519,13 @@ int nv50_display_create(struct drm_device *dev)
                        NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
                        continue;
                }
-
-               connector[entry->connector] |= (1 << entry->type);
        }
 
-       /* It appears that DCB 3.0+ VBIOS has a connector table, however,
-        * I'm not 100% certain how to decode it correctly yet so just
-        * look at what encoders are present on each connector index and
-        * attempt to derive the connector type from that.
-        */
-       for (i = 0 ; i < dcb->entries; i++) {
-               struct dcb_entry *entry = &dcb->entry[i];
-               uint16_t encoders;
-               int type;
-
-               encoders = connector[entry->connector];
-               if (!(encoders & (1 << entry->type)))
+       for (i = 0 ; i < dcb->connector.entries; i++) {
+               if (i != 0 && dcb->connector.entry[i].index ==
+                             dcb->connector.entry[i - 1].index)
                        continue;
-               connector[entry->connector] = 0;
-
-               if (encoders & (1 << OUTPUT_DP)) {
-                       type = DRM_MODE_CONNECTOR_DisplayPort;
-               } else if (encoders & (1 << OUTPUT_TMDS)) {
-                       if (encoders & (1 << OUTPUT_ANALOG))
-                               type = DRM_MODE_CONNECTOR_DVII;
-                       else
-                               type = DRM_MODE_CONNECTOR_DVID;
-               } else if (encoders & (1 << OUTPUT_ANALOG)) {
-                       type = DRM_MODE_CONNECTOR_VGA;
-               } else if (encoders & (1 << OUTPUT_LVDS)) {
-                       type = DRM_MODE_CONNECTOR_LVDS;
-               } else {
-                       type = DRM_MODE_CONNECTOR_Unknown;
-               }
-
-               if (type == DRM_MODE_CONNECTOR_Unknown)
-                       continue;
-
-               nouveau_connector_create(dev, entry->connector, type);
+               nouveau_connector_create(dev, &dcb->connector.entry[i]);
        }
 
        ret = nv50_display_init(dev);
@@ -667,8 +633,8 @@ nv50_display_irq_head(struct drm_device *dev, int *phead,
                return -1;
        }
 
-       for (i = 0; i < dev_priv->vbios->dcb->entries; i++) {
-               struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i];
+       for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
+               struct dcb_entry *dcbent = &dev_priv->vbios.dcb.entry[i];
 
                if (dcbent->type != type)
                        continue;
@@ -692,7 +658,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_connector *nv_connector = NULL;
        struct drm_encoder *encoder;
-       struct nvbios *bios = &dev_priv->VBIOS;
+       struct nvbios *bios = &dev_priv->vbios;
        uint32_t mc, script = 0, or;
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
@@ -710,7 +676,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
        switch (dcbent->type) {
        case OUTPUT_LVDS:
                script = (mc >> 8) & 0xf;
-               if (bios->pub.fp_no_ddc) {
+               if (bios->fp_no_ddc) {
                        if (bios->fp.dual_link)
                                script |= 0x0100;
                        if (bios->fp.if_is_24bit)
index 0f57cdf7ccb23dd22ed70d581247f23e14db6596..993c7126fbded104b6ed3a16cac2e5f07f757474 100644 (file)
@@ -109,7 +109,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
                return;
        }
 
-       width = (image->width + 31) & ~31;
+       width = ALIGN(image->width, 32);
        dwords = (width * image->height) >> 5;
 
        BEGIN_RING(chan, NvSub2D, 0x0814, 2);
index 204a79ff10f4e529479bc0136f4209282e085d8c..e20c0e2474f3b56035b3c6391b86a17e4c693f0f 100644 (file)
@@ -243,6 +243,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
        struct drm_device *dev = chan->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_gpuobj *ramfc = NULL;
+       unsigned long flags;
        int ret;
 
        NV_DEBUG(dev, "ch%d\n", chan->id);
@@ -278,19 +279,21 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
                        return ret;
        }
 
+       spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
        dev_priv->engine.instmem.prepare_access(dev, true);
 
-       nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
-       nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base);
        nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
        nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
-       nv_wo32(dev, ramfc, 0x3c/4, 0x00086078);
        nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
        nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
        nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
        nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
        nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
-       nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff);
+       nv_wo32(dev, ramfc, 0x3c/4, 0x403f6078);
+       nv_wo32(dev, ramfc, 0x50/4, chan->pushbuf_base +
+                                   chan->dma.ib_base * 4);
+       nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16);
 
        if (!IS_G80) {
                nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
@@ -306,10 +309,12 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
        ret = nv50_fifo_channel_enable(dev, chan->id, false);
        if (ret) {
                NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
+               spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
                nouveau_gpuobj_ref_del(dev, &chan->ramfc);
                return ret;
        }
 
+       spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
        return 0;
 }
 
index 6d504801b514823d09575b51e644395933d53122..857a09671a394c80a534d350b659261d8f56c223 100644 (file)
 #include "drm.h"
 #include "nouveau_drv.h"
 
-MODULE_FIRMWARE("nouveau/nv50.ctxprog");
-MODULE_FIRMWARE("nouveau/nv50.ctxvals");
-MODULE_FIRMWARE("nouveau/nv84.ctxprog");
-MODULE_FIRMWARE("nouveau/nv84.ctxvals");
-MODULE_FIRMWARE("nouveau/nv86.ctxprog");
-MODULE_FIRMWARE("nouveau/nv86.ctxvals");
-MODULE_FIRMWARE("nouveau/nv92.ctxprog");
-MODULE_FIRMWARE("nouveau/nv92.ctxvals");
-MODULE_FIRMWARE("nouveau/nv94.ctxprog");
-MODULE_FIRMWARE("nouveau/nv94.ctxvals");
-MODULE_FIRMWARE("nouveau/nv96.ctxprog");
-MODULE_FIRMWARE("nouveau/nv96.ctxvals");
-MODULE_FIRMWARE("nouveau/nv98.ctxprog");
-MODULE_FIRMWARE("nouveau/nv98.ctxvals");
-MODULE_FIRMWARE("nouveau/nva0.ctxprog");
-MODULE_FIRMWARE("nouveau/nva0.ctxvals");
-MODULE_FIRMWARE("nouveau/nva5.ctxprog");
-MODULE_FIRMWARE("nouveau/nva5.ctxvals");
-MODULE_FIRMWARE("nouveau/nva8.ctxprog");
-MODULE_FIRMWARE("nouveau/nva8.ctxvals");
-MODULE_FIRMWARE("nouveau/nvaa.ctxprog");
-MODULE_FIRMWARE("nouveau/nvaa.ctxvals");
-MODULE_FIRMWARE("nouveau/nvac.ctxprog");
-MODULE_FIRMWARE("nouveau/nvac.ctxvals");
+#include "nouveau_grctx.h"
 
 #define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
 
@@ -111,9 +88,34 @@ nv50_graph_init_ctxctl(struct drm_device *dev)
 
        NV_DEBUG(dev, "\n");
 
-       nouveau_grctx_prog_load(dev);
-       if (!dev_priv->engine.graph.ctxprog)
-               dev_priv->engine.graph.accel_blocked = true;
+       if (nouveau_ctxfw) {
+               nouveau_grctx_prog_load(dev);
+               dev_priv->engine.graph.grctx_size = 0x70000;
+       }
+       if (!dev_priv->engine.graph.ctxprog) {
+               struct nouveau_grctx ctx = {};
+               uint32_t *cp = kmalloc(512 * 4, GFP_KERNEL);
+               int i;
+               if (!cp) {
+                       NV_ERROR(dev, "Couldn't alloc ctxprog! Disabling acceleration.\n");
+                       dev_priv->engine.graph.accel_blocked = true;
+                       return 0;
+               }
+               ctx.dev = dev;
+               ctx.mode = NOUVEAU_GRCTX_PROG;
+               ctx.data = cp;
+               ctx.ctxprog_max = 512;
+               if (!nv50_grctx_init(&ctx)) {
+                       dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
+
+                       nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
+                       for (i = 0; i < ctx.ctxprog_len; i++)
+                               nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
+               } else {
+                       dev_priv->engine.graph.accel_blocked = true;
+               }
+               kfree(cp);
+       }
 
        nv_wr32(dev, 0x400320, 4);
        nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
@@ -193,13 +195,13 @@ nv50_graph_create_context(struct nouveau_channel *chan)
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
        struct nouveau_gpuobj *ctx;
-       uint32_t grctx_size = 0x70000;
+       struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
        int hdr, ret;
 
        NV_DEBUG(dev, "ch%d\n", chan->id);
 
-       ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000,
-                                    NVOBJ_FLAG_ZERO_ALLOC |
+       ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
+                                    0x1000, NVOBJ_FLAG_ZERO_ALLOC |
                                     NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
        if (ret)
                return ret;
@@ -209,7 +211,7 @@ nv50_graph_create_context(struct nouveau_channel *chan)
        dev_priv->engine.instmem.prepare_access(dev, true);
        nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
        nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
-                                          grctx_size - 1);
+                                          pgraph->grctx_size - 1);
        nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance);
        nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
        nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
@@ -217,7 +219,15 @@ nv50_graph_create_context(struct nouveau_channel *chan)
        dev_priv->engine.instmem.finish_access(dev);
 
        dev_priv->engine.instmem.prepare_access(dev, true);
-       nouveau_grctx_vals_load(dev, ctx);
+       if (!pgraph->ctxprog) {
+               struct nouveau_grctx ctx = {};
+               ctx.dev = chan->dev;
+               ctx.mode = NOUVEAU_GRCTX_VALS;
+               ctx.data = chan->ramin_grctx->gpuobj;
+               nv50_grctx_init(&ctx);
+       } else {
+               nouveau_grctx_vals_load(dev, ctx);
+       }
        nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
        if ((dev_priv->chipset & 0xf0) == 0xa0)
                nv_wo32(dev, ctx, 0x00004/4, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c
new file mode 100644 (file)
index 0000000..d105fcd
--- /dev/null
@@ -0,0 +1,2367 @@
+/*
+ * Copyright 2009 Marcin KoÅ›cielnicki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#define CP_FLAG_CLEAR                 0
+#define CP_FLAG_SET                   1
+#define CP_FLAG_SWAP_DIRECTION        ((0 * 32) + 0)
+#define CP_FLAG_SWAP_DIRECTION_LOAD   0
+#define CP_FLAG_SWAP_DIRECTION_SAVE   1
+#define CP_FLAG_UNK01                 ((0 * 32) + 1)
+#define CP_FLAG_UNK01_CLEAR           0
+#define CP_FLAG_UNK01_SET             1
+#define CP_FLAG_UNK03                 ((0 * 32) + 3)
+#define CP_FLAG_UNK03_CLEAR           0
+#define CP_FLAG_UNK03_SET             1
+#define CP_FLAG_USER_SAVE             ((0 * 32) + 5)
+#define CP_FLAG_USER_SAVE_NOT_PENDING 0
+#define CP_FLAG_USER_SAVE_PENDING     1
+#define CP_FLAG_USER_LOAD             ((0 * 32) + 6)
+#define CP_FLAG_USER_LOAD_NOT_PENDING 0
+#define CP_FLAG_USER_LOAD_PENDING     1
+#define CP_FLAG_UNK0B                 ((0 * 32) + 0xb)
+#define CP_FLAG_UNK0B_CLEAR           0
+#define CP_FLAG_UNK0B_SET             1
+#define CP_FLAG_UNK1D                 ((0 * 32) + 0x1d)
+#define CP_FLAG_UNK1D_CLEAR           0
+#define CP_FLAG_UNK1D_SET             1
+#define CP_FLAG_UNK20                 ((1 * 32) + 0)
+#define CP_FLAG_UNK20_CLEAR           0
+#define CP_FLAG_UNK20_SET             1
+#define CP_FLAG_STATUS                ((2 * 32) + 0)
+#define CP_FLAG_STATUS_BUSY           0
+#define CP_FLAG_STATUS_IDLE           1
+#define CP_FLAG_AUTO_SAVE             ((2 * 32) + 4)
+#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0
+#define CP_FLAG_AUTO_SAVE_PENDING     1
+#define CP_FLAG_AUTO_LOAD             ((2 * 32) + 5)
+#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0
+#define CP_FLAG_AUTO_LOAD_PENDING     1
+#define CP_FLAG_XFER                  ((2 * 32) + 11)
+#define CP_FLAG_XFER_IDLE             0
+#define CP_FLAG_XFER_BUSY             1
+#define CP_FLAG_NEWCTX                ((2 * 32) + 12)
+#define CP_FLAG_NEWCTX_BUSY           0
+#define CP_FLAG_NEWCTX_DONE           1
+#define CP_FLAG_ALWAYS                ((2 * 32) + 13)
+#define CP_FLAG_ALWAYS_FALSE          0
+#define CP_FLAG_ALWAYS_TRUE           1
+
+#define CP_CTX                   0x00100000
+#define CP_CTX_COUNT             0x000f0000
+#define CP_CTX_COUNT_SHIFT               16
+#define CP_CTX_REG               0x00003fff
+#define CP_LOAD_SR               0x00200000
+#define CP_LOAD_SR_VALUE         0x000fffff
+#define CP_BRA                   0x00400000
+#define CP_BRA_IP                0x0001ff00
+#define CP_BRA_IP_SHIFT                   8
+#define CP_BRA_IF_CLEAR          0x00000080
+#define CP_BRA_FLAG              0x0000007f
+#define CP_WAIT                  0x00500000
+#define CP_WAIT_SET              0x00000080
+#define CP_WAIT_FLAG             0x0000007f
+#define CP_SET                   0x00700000
+#define CP_SET_1                 0x00000080
+#define CP_SET_FLAG              0x0000007f
+#define CP_NEWCTX                0x00600004
+#define CP_NEXT_TO_SWAP          0x00600005
+#define CP_SET_CONTEXT_POINTER   0x00600006
+#define CP_SET_XFER_POINTER      0x00600007
+#define CP_ENABLE                0x00600009
+#define CP_END                   0x0060000c
+#define CP_NEXT_TO_CURRENT       0x0060000d
+#define CP_DISABLE1              0x0090ffff
+#define CP_DISABLE2              0x0091ffff
+#define CP_XFER_1      0x008000ff
+#define CP_XFER_2      0x008800ff
+#define CP_SEEK_1      0x00c000ff
+#define CP_SEEK_2      0x00c800ff
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_grctx.h"
+
+/*
+ * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's
+ * the GPU itself that does context-switching, but it needs a special
+ * microcode to do it. And it's the driver's task to supply this microcode,
+ * further known as ctxprog, as well as the initial context values, known
+ * as ctxvals.
+ *
+ * Without ctxprog, you cannot switch contexts. Not even in software, since
+ * the majority of context [xfer strands] isn't accessible directly. You're
+ * stuck with a single channel, and you also suffer all the problems resulting
+ * from missing ctxvals, since you cannot load them.
+ *
+ * Without ctxvals, you're stuck with PGRAPH's default context. It's enough to
+ * run 2d operations, but trying to utilise 3d or CUDA will just lock you up,
+ * since you don't have... some sort of needed setup.
+ *
+ * Nouveau will just disable acceleration if not given ctxprog + ctxvals, since
+ * it's too much hassle to handle no-ctxprog as a special case.
+ */
+
+/*
+ * How ctxprogs work.
+ *
+ * The ctxprog is written in its own kind of microcode, with very small and
+ * crappy set of available commands. You upload it to a small [512 insns]
+ * area of memory on PGRAPH, and it'll be run when PFIFO wants PGRAPH to
+ * switch channel. or when the driver explicitely requests it. Stuff visible
+ * to ctxprog consists of: PGRAPH MMIO registers, PGRAPH context strands,
+ * the per-channel context save area in VRAM [known as ctxvals or grctx],
+ * 4 flags registers, a scratch register, two grctx pointers, plus many
+ * random poorly-understood details.
+ *
+ * When ctxprog runs, it's supposed to check what operations are asked of it,
+ * save old context if requested, optionally reset PGRAPH and switch to the
+ * new channel, and load the new context. Context consists of three major
+ * parts: subset of MMIO registers and two "xfer areas".
+ */
+
+/* TODO:
+ *  - document unimplemented bits compared to nvidia
+ *  - NVAx: make a TP subroutine, use it.
+ *  - use 0x4008fc instead of 0x1540?
+ */
+
+enum cp_label {
+       cp_check_load = 1,
+       cp_setup_auto_load,
+       cp_setup_load,
+       cp_setup_save,
+       cp_swap_state,
+       cp_prepare_exit,
+       cp_exit,
+};
+
+static void nv50_graph_construct_mmio(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_xfer1(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_xfer2(struct nouveau_grctx *ctx);
+
+/* Main function: construct the ctxprog skeleton, call the other functions. */
+
+int
+nv50_grctx_init(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+
+       switch (dev_priv->chipset) {
+       case 0x50:
+       case 0x84:
+       case 0x86:
+       case 0x92:
+       case 0x94:
+       case 0x96:
+       case 0x98:
+       case 0xa0:
+       case 0xa5:
+       case 0xa8:
+       case 0xaa:
+       case 0xac:
+               break;
+       default:
+               NV_ERROR(ctx->dev, "I don't know how to make a ctxprog for "
+                                  "your NV%x card.\n", dev_priv->chipset);
+               NV_ERROR(ctx->dev, "Disabling acceleration. Please contact "
+                                  "the devs.\n");
+               return -ENOSYS;
+       }
+       /* decide whether we're loading/unloading the context */
+       cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save);
+       cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save);
+
+       cp_name(ctx, cp_check_load);
+       cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load);
+       cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load);
+       cp_bra (ctx, ALWAYS, TRUE, cp_exit);
+
+       /* setup for context load */
+       cp_name(ctx, cp_setup_auto_load);
+       cp_out (ctx, CP_DISABLE1);
+       cp_out (ctx, CP_DISABLE2);
+       cp_out (ctx, CP_ENABLE);
+       cp_out (ctx, CP_NEXT_TO_SWAP);
+       cp_set (ctx, UNK01, SET);
+       cp_name(ctx, cp_setup_load);
+       cp_out (ctx, CP_NEWCTX);
+       cp_wait(ctx, NEWCTX, BUSY);
+       cp_set (ctx, UNK1D, CLEAR);
+       cp_set (ctx, SWAP_DIRECTION, LOAD);
+       cp_bra (ctx, UNK0B, SET, cp_prepare_exit);
+       cp_bra (ctx, ALWAYS, TRUE, cp_swap_state);
+
+       /* setup for context save */
+       cp_name(ctx, cp_setup_save);
+       cp_set (ctx, UNK1D, SET);
+       cp_wait(ctx, STATUS, BUSY);
+       cp_set (ctx, UNK01, SET);
+       cp_set (ctx, SWAP_DIRECTION, SAVE);
+
+       /* general PGRAPH state */
+       cp_name(ctx, cp_swap_state);
+       cp_set (ctx, UNK03, SET);
+       cp_pos (ctx, 0x00004/4);
+       cp_ctx (ctx, 0x400828, 1); /* needed. otherwise, flickering happens. */
+       cp_pos (ctx, 0x00100/4);
+       nv50_graph_construct_mmio(ctx);
+       nv50_graph_construct_xfer1(ctx);
+       nv50_graph_construct_xfer2(ctx);
+
+       cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load);
+
+       cp_set (ctx, UNK20, SET);
+       cp_set (ctx, SWAP_DIRECTION, SAVE); /* no idea why this is needed, but fixes at least one lockup. */
+       cp_lsr (ctx, ctx->ctxvals_base);
+       cp_out (ctx, CP_SET_XFER_POINTER);
+       cp_lsr (ctx, 4);
+       cp_out (ctx, CP_SEEK_1);
+       cp_out (ctx, CP_XFER_1);
+       cp_wait(ctx, XFER, BUSY);
+
+       /* pre-exit state updates */
+       cp_name(ctx, cp_prepare_exit);
+       cp_set (ctx, UNK01, CLEAR);
+       cp_set (ctx, UNK03, CLEAR);
+       cp_set (ctx, UNK1D, CLEAR);
+
+       cp_bra (ctx, USER_SAVE, PENDING, cp_exit);
+       cp_out (ctx, CP_NEXT_TO_CURRENT);
+
+       cp_name(ctx, cp_exit);
+       cp_set (ctx, USER_SAVE, NOT_PENDING);
+       cp_set (ctx, USER_LOAD, NOT_PENDING);
+       cp_out (ctx, CP_END);
+       ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */
+
+       return 0;
+}
+
+/*
+ * Constructs MMIO part of ctxprog and ctxvals. Just a matter of knowing which
+ * registers to save/restore and the default values for them.
+ */
+
+static void
+nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int i, j;
+       int offset, base;
+       uint32_t units = nv_rd32 (ctx->dev, 0x1540);
+
+       /* 0800 */
+       cp_ctx(ctx, 0x400808, 7);
+       gr_def(ctx, 0x400814, 0x00000030);
+       cp_ctx(ctx, 0x400834, 0x32);
+       if (dev_priv->chipset == 0x50) {
+               gr_def(ctx, 0x400834, 0xff400040);
+               gr_def(ctx, 0x400838, 0xfff00080);
+               gr_def(ctx, 0x40083c, 0xfff70090);
+               gr_def(ctx, 0x400840, 0xffe806a8);
+       }
+       gr_def(ctx, 0x400844, 0x00000002);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               gr_def(ctx, 0x400894, 0x00001000);
+       gr_def(ctx, 0x4008e8, 0x00000003);
+       gr_def(ctx, 0x4008ec, 0x00001000);
+       if (dev_priv->chipset == 0x50)
+               cp_ctx(ctx, 0x400908, 0xb);
+       else if (dev_priv->chipset < 0xa0)
+               cp_ctx(ctx, 0x400908, 0xc);
+       else
+               cp_ctx(ctx, 0x400908, 0xe);
+
+       if (dev_priv->chipset >= 0xa0)
+               cp_ctx(ctx, 0x400b00, 0x1);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               cp_ctx(ctx, 0x400b10, 0x1);
+               gr_def(ctx, 0x400b10, 0x0001629d);
+               cp_ctx(ctx, 0x400b20, 0x1);
+               gr_def(ctx, 0x400b20, 0x0001629d);
+       }
+
+       /* 0C00 */
+       cp_ctx(ctx, 0x400c08, 0x2);
+       gr_def(ctx, 0x400c08, 0x0000fe0c);
+
+       /* 1000 */
+       if (dev_priv->chipset < 0xa0) {
+               cp_ctx(ctx, 0x401008, 0x4);
+               gr_def(ctx, 0x401014, 0x00001000);
+       } else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) {
+               cp_ctx(ctx, 0x401008, 0x5);
+               gr_def(ctx, 0x401018, 0x00001000);
+       } else {
+               cp_ctx(ctx, 0x401008, 0x5);
+               gr_def(ctx, 0x401018, 0x00004000);
+       }
+
+       /* 1400 */
+       cp_ctx(ctx, 0x401400, 0x8);
+       cp_ctx(ctx, 0x401424, 0x3);
+       if (dev_priv->chipset == 0x50)
+               gr_def(ctx, 0x40142c, 0x0001fd87);
+       else
+               gr_def(ctx, 0x40142c, 0x00000187);
+       cp_ctx(ctx, 0x401540, 0x5);
+       gr_def(ctx, 0x401550, 0x00001018);
+
+       /* 1800 */
+       cp_ctx(ctx, 0x401814, 0x1);
+       gr_def(ctx, 0x401814, 0x000000ff);
+       if (dev_priv->chipset == 0x50) {
+               cp_ctx(ctx, 0x40181c, 0xe);
+               gr_def(ctx, 0x401850, 0x00000004);
+       } else if (dev_priv->chipset < 0xa0) {
+               cp_ctx(ctx, 0x40181c, 0xf);
+               gr_def(ctx, 0x401854, 0x00000004);
+       } else {
+               cp_ctx(ctx, 0x40181c, 0x13);
+               gr_def(ctx, 0x401864, 0x00000004);
+       }
+
+       /* 1C00 */
+       cp_ctx(ctx, 0x401c00, 0x1);
+       switch (dev_priv->chipset) {
+       case 0x50:
+               gr_def(ctx, 0x401c00, 0x0001005f);
+               break;
+       case 0x84:
+       case 0x86:
+       case 0x94:
+               gr_def(ctx, 0x401c00, 0x044d00df);
+               break;
+       case 0x92:
+       case 0x96:
+       case 0x98:
+       case 0xa0:
+       case 0xaa:
+       case 0xac:
+               gr_def(ctx, 0x401c00, 0x042500df);
+               break;
+       case 0xa5:
+       case 0xa8:
+               gr_def(ctx, 0x401c00, 0x142500df);
+               break;
+       }
+
+       /* 2400 */
+       cp_ctx(ctx, 0x402400, 0x1);
+       if (dev_priv->chipset == 0x50)
+               cp_ctx(ctx, 0x402408, 0x1);
+       else
+               cp_ctx(ctx, 0x402408, 0x2);
+       gr_def(ctx, 0x402408, 0x00000600);
+
+       /* 2800 */
+       cp_ctx(ctx, 0x402800, 0x1);
+       if (dev_priv->chipset == 0x50)
+               gr_def(ctx, 0x402800, 0x00000006);
+
+       /* 2C00 */
+       cp_ctx(ctx, 0x402c08, 0x6);
+       if (dev_priv->chipset != 0x50)
+               gr_def(ctx, 0x402c14, 0x01000000);
+       gr_def(ctx, 0x402c18, 0x000000ff);
+       if (dev_priv->chipset == 0x50)
+               cp_ctx(ctx, 0x402ca0, 0x1);
+       else
+               cp_ctx(ctx, 0x402ca0, 0x2);
+       if (dev_priv->chipset < 0xa0)
+               gr_def(ctx, 0x402ca0, 0x00000400);
+       else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa)
+               gr_def(ctx, 0x402ca0, 0x00000800);
+       else
+               gr_def(ctx, 0x402ca0, 0x00000400);
+       cp_ctx(ctx, 0x402cac, 0x4);
+
+       /* 3000 */
+       cp_ctx(ctx, 0x403004, 0x1);
+       gr_def(ctx, 0x403004, 0x00000001);
+
+       /* 3404 */
+       if (dev_priv->chipset >= 0xa0) {
+               cp_ctx(ctx, 0x403404, 0x1);
+               gr_def(ctx, 0x403404, 0x00000001);
+       }
+
+       /* 5000 */
+       cp_ctx(ctx, 0x405000, 0x1);
+       switch (dev_priv->chipset) {
+       case 0x50:
+               gr_def(ctx, 0x405000, 0x00300080);
+               break;
+       case 0x84:
+       case 0xa0:
+       case 0xa5:
+       case 0xa8:
+       case 0xaa:
+       case 0xac:
+               gr_def(ctx, 0x405000, 0x000e0080);
+               break;
+       case 0x86:
+       case 0x92:
+       case 0x94:
+       case 0x96:
+       case 0x98:
+               gr_def(ctx, 0x405000, 0x00000080);
+               break;
+       }
+       cp_ctx(ctx, 0x405014, 0x1);
+       gr_def(ctx, 0x405014, 0x00000004);
+       cp_ctx(ctx, 0x40501c, 0x1);
+       cp_ctx(ctx, 0x405024, 0x1);
+       cp_ctx(ctx, 0x40502c, 0x1);
+
+       /* 5400 or maybe 4800 */
+       if (dev_priv->chipset == 0x50) {
+               offset = 0x405400;
+               cp_ctx(ctx, 0x405400, 0xea);
+       } else if (dev_priv->chipset < 0x94) {
+               offset = 0x405400;
+               cp_ctx(ctx, 0x405400, 0xcb);
+       } else if (dev_priv->chipset < 0xa0) {
+               offset = 0x405400;
+               cp_ctx(ctx, 0x405400, 0xcc);
+       } else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               offset = 0x404800;
+               cp_ctx(ctx, 0x404800, 0xda);
+       } else {
+               offset = 0x405400;
+               cp_ctx(ctx, 0x405400, 0xd4);
+       }
+       gr_def(ctx, offset + 0x0c, 0x00000002);
+       gr_def(ctx, offset + 0x10, 0x00000001);
+       if (dev_priv->chipset >= 0x94)
+               offset += 4;
+       gr_def(ctx, offset + 0x1c, 0x00000001);
+       gr_def(ctx, offset + 0x20, 0x00000100);
+       gr_def(ctx, offset + 0x38, 0x00000002);
+       gr_def(ctx, offset + 0x3c, 0x00000001);
+       gr_def(ctx, offset + 0x40, 0x00000001);
+       gr_def(ctx, offset + 0x50, 0x00000001);
+       gr_def(ctx, offset + 0x54, 0x003fffff);
+       gr_def(ctx, offset + 0x58, 0x00001fff);
+       gr_def(ctx, offset + 0x60, 0x00000001);
+       gr_def(ctx, offset + 0x64, 0x00000001);
+       gr_def(ctx, offset + 0x6c, 0x00000001);
+       gr_def(ctx, offset + 0x70, 0x00000001);
+       gr_def(ctx, offset + 0x74, 0x00000001);
+       gr_def(ctx, offset + 0x78, 0x00000004);
+       gr_def(ctx, offset + 0x7c, 0x00000001);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               offset += 4;
+       gr_def(ctx, offset + 0x80, 0x00000001);
+       gr_def(ctx, offset + 0x84, 0x00000001);
+       gr_def(ctx, offset + 0x88, 0x00000007);
+       gr_def(ctx, offset + 0x8c, 0x00000001);
+       gr_def(ctx, offset + 0x90, 0x00000007);
+       gr_def(ctx, offset + 0x94, 0x00000001);
+       gr_def(ctx, offset + 0x98, 0x00000001);
+       gr_def(ctx, offset + 0x9c, 0x00000001);
+       if (dev_priv->chipset == 0x50) {
+                gr_def(ctx, offset + 0xb0, 0x00000001);
+                gr_def(ctx, offset + 0xb4, 0x00000001);
+                gr_def(ctx, offset + 0xbc, 0x00000001);
+                gr_def(ctx, offset + 0xc0, 0x0000000a);
+                gr_def(ctx, offset + 0xd0, 0x00000040);
+                gr_def(ctx, offset + 0xd8, 0x00000002);
+                gr_def(ctx, offset + 0xdc, 0x00000100);
+                gr_def(ctx, offset + 0xe0, 0x00000001);
+                gr_def(ctx, offset + 0xe4, 0x00000100);
+                gr_def(ctx, offset + 0x100, 0x00000001);
+                gr_def(ctx, offset + 0x124, 0x00000004);
+                gr_def(ctx, offset + 0x13c, 0x00000001);
+                gr_def(ctx, offset + 0x140, 0x00000100);
+                gr_def(ctx, offset + 0x148, 0x00000001);
+                gr_def(ctx, offset + 0x154, 0x00000100);
+                gr_def(ctx, offset + 0x158, 0x00000001);
+                gr_def(ctx, offset + 0x15c, 0x00000100);
+                gr_def(ctx, offset + 0x164, 0x00000001);
+                gr_def(ctx, offset + 0x170, 0x00000100);
+                gr_def(ctx, offset + 0x174, 0x00000001);
+                gr_def(ctx, offset + 0x17c, 0x00000001);
+                gr_def(ctx, offset + 0x188, 0x00000002);
+                gr_def(ctx, offset + 0x190, 0x00000001);
+                gr_def(ctx, offset + 0x198, 0x00000001);
+                gr_def(ctx, offset + 0x1ac, 0x00000003);
+                offset += 0xd0;
+       } else {
+               gr_def(ctx, offset + 0xb0, 0x00000001);
+               gr_def(ctx, offset + 0xb4, 0x00000100);
+               gr_def(ctx, offset + 0xbc, 0x00000001);
+               gr_def(ctx, offset + 0xc8, 0x00000100);
+               gr_def(ctx, offset + 0xcc, 0x00000001);
+               gr_def(ctx, offset + 0xd0, 0x00000100);
+               gr_def(ctx, offset + 0xd8, 0x00000001);
+               gr_def(ctx, offset + 0xe4, 0x00000100);
+       }
+       gr_def(ctx, offset + 0xf8, 0x00000004);
+       gr_def(ctx, offset + 0xfc, 0x00000070);
+       gr_def(ctx, offset + 0x100, 0x00000080);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               offset += 4;
+       gr_def(ctx, offset + 0x114, 0x0000000c);
+       if (dev_priv->chipset == 0x50)
+               offset -= 4;
+       gr_def(ctx, offset + 0x11c, 0x00000008);
+       gr_def(ctx, offset + 0x120, 0x00000014);
+       if (dev_priv->chipset == 0x50) {
+               gr_def(ctx, offset + 0x124, 0x00000026);
+               offset -= 0x18;
+       } else {
+               gr_def(ctx, offset + 0x128, 0x00000029);
+               gr_def(ctx, offset + 0x12c, 0x00000027);
+               gr_def(ctx, offset + 0x130, 0x00000026);
+               gr_def(ctx, offset + 0x134, 0x00000008);
+               gr_def(ctx, offset + 0x138, 0x00000004);
+               gr_def(ctx, offset + 0x13c, 0x00000027);
+       }
+       gr_def(ctx, offset + 0x148, 0x00000001);
+       gr_def(ctx, offset + 0x14c, 0x00000002);
+       gr_def(ctx, offset + 0x150, 0x00000003);
+       gr_def(ctx, offset + 0x154, 0x00000004);
+       gr_def(ctx, offset + 0x158, 0x00000005);
+       gr_def(ctx, offset + 0x15c, 0x00000006);
+       gr_def(ctx, offset + 0x160, 0x00000007);
+       gr_def(ctx, offset + 0x164, 0x00000001);
+       gr_def(ctx, offset + 0x1a8, 0x000000cf);
+       if (dev_priv->chipset == 0x50)
+               offset -= 4;
+       gr_def(ctx, offset + 0x1d8, 0x00000080);
+       gr_def(ctx, offset + 0x1dc, 0x00000004);
+       gr_def(ctx, offset + 0x1e0, 0x00000004);
+       if (dev_priv->chipset == 0x50)
+               offset -= 4;
+       else
+               gr_def(ctx, offset + 0x1e4, 0x00000003);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               gr_def(ctx, offset + 0x1ec, 0x00000003);
+               offset += 8;
+       }
+       gr_def(ctx, offset + 0x1e8, 0x00000001);
+       if (dev_priv->chipset == 0x50)
+               offset -= 4;
+       gr_def(ctx, offset + 0x1f4, 0x00000012);
+       gr_def(ctx, offset + 0x1f8, 0x00000010);
+       gr_def(ctx, offset + 0x1fc, 0x0000000c);
+       gr_def(ctx, offset + 0x200, 0x00000001);
+       gr_def(ctx, offset + 0x210, 0x00000004);
+       gr_def(ctx, offset + 0x214, 0x00000002);
+       gr_def(ctx, offset + 0x218, 0x00000004);
+       if (dev_priv->chipset >= 0xa0)
+               offset += 4;
+       gr_def(ctx, offset + 0x224, 0x003fffff);
+       gr_def(ctx, offset + 0x228, 0x00001fff);
+       if (dev_priv->chipset == 0x50)
+               offset -= 0x20;
+       else if (dev_priv->chipset >= 0xa0) {
+               gr_def(ctx, offset + 0x250, 0x00000001);
+               gr_def(ctx, offset + 0x254, 0x00000001);
+               gr_def(ctx, offset + 0x258, 0x00000002);
+               offset += 0x10;
+       }
+       gr_def(ctx, offset + 0x250, 0x00000004);
+       gr_def(ctx, offset + 0x254, 0x00000014);
+       gr_def(ctx, offset + 0x258, 0x00000001);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               offset += 4;
+       gr_def(ctx, offset + 0x264, 0x00000002);
+       if (dev_priv->chipset >= 0xa0)
+               offset += 8;
+       gr_def(ctx, offset + 0x270, 0x00000001);
+       gr_def(ctx, offset + 0x278, 0x00000002);
+       gr_def(ctx, offset + 0x27c, 0x00001000);
+       if (dev_priv->chipset == 0x50)
+               offset -= 0xc;
+       else {
+               gr_def(ctx, offset + 0x280, 0x00000e00);
+               gr_def(ctx, offset + 0x284, 0x00001000);
+               gr_def(ctx, offset + 0x288, 0x00001e00);
+       }
+       gr_def(ctx, offset + 0x290, 0x00000001);
+       gr_def(ctx, offset + 0x294, 0x00000001);
+       gr_def(ctx, offset + 0x298, 0x00000001);
+       gr_def(ctx, offset + 0x29c, 0x00000001);
+       gr_def(ctx, offset + 0x2a0, 0x00000001);
+       gr_def(ctx, offset + 0x2b0, 0x00000200);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               gr_def(ctx, offset + 0x2b4, 0x00000200);
+               offset += 4;
+       }
+       if (dev_priv->chipset < 0xa0) {
+               gr_def(ctx, offset + 0x2b8, 0x00000001);
+               gr_def(ctx, offset + 0x2bc, 0x00000070);
+               gr_def(ctx, offset + 0x2c0, 0x00000080);
+               gr_def(ctx, offset + 0x2cc, 0x00000001);
+               gr_def(ctx, offset + 0x2d0, 0x00000070);
+               gr_def(ctx, offset + 0x2d4, 0x00000080);
+       } else {
+               gr_def(ctx, offset + 0x2b8, 0x00000001);
+               gr_def(ctx, offset + 0x2bc, 0x000000f0);
+               gr_def(ctx, offset + 0x2c0, 0x000000ff);
+               gr_def(ctx, offset + 0x2cc, 0x00000001);
+               gr_def(ctx, offset + 0x2d0, 0x000000f0);
+               gr_def(ctx, offset + 0x2d4, 0x000000ff);
+               gr_def(ctx, offset + 0x2dc, 0x00000009);
+               offset += 4;
+       }
+       gr_def(ctx, offset + 0x2e4, 0x00000001);
+       gr_def(ctx, offset + 0x2e8, 0x000000cf);
+       gr_def(ctx, offset + 0x2f0, 0x00000001);
+       gr_def(ctx, offset + 0x300, 0x000000cf);
+       gr_def(ctx, offset + 0x308, 0x00000002);
+       gr_def(ctx, offset + 0x310, 0x00000001);
+       gr_def(ctx, offset + 0x318, 0x00000001);
+       gr_def(ctx, offset + 0x320, 0x000000cf);
+       gr_def(ctx, offset + 0x324, 0x000000cf);
+       gr_def(ctx, offset + 0x328, 0x00000001);
+
+       /* 6000? */
+       if (dev_priv->chipset == 0x50)
+               cp_ctx(ctx, 0x4063e0, 0x1);
+
+       /* 6800 */
+       if (dev_priv->chipset < 0x90) {
+               cp_ctx(ctx, 0x406814, 0x2b);
+               gr_def(ctx, 0x406818, 0x00000f80);
+               gr_def(ctx, 0x406860, 0x007f0080);
+               gr_def(ctx, 0x40689c, 0x007f0080);
+       } else {
+               cp_ctx(ctx, 0x406814, 0x4);
+               if (dev_priv->chipset == 0x98)
+                       gr_def(ctx, 0x406818, 0x00000f80);
+               else
+                       gr_def(ctx, 0x406818, 0x00001f80);
+               if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+                       gr_def(ctx, 0x40681c, 0x00000030);
+               cp_ctx(ctx, 0x406830, 0x3);
+       }
+
+       /* 7000: per-ROP group state */
+       for (i = 0; i < 8; i++) {
+               if (units & (1<<(i+16))) {
+                       cp_ctx(ctx, 0x407000 + (i<<8), 3);
+                       if (dev_priv->chipset == 0x50)
+                               gr_def(ctx, 0x407000 + (i<<8), 0x1b74f820);
+                       else if (dev_priv->chipset != 0xa5)
+                               gr_def(ctx, 0x407000 + (i<<8), 0x3b74f821);
+                       else
+                               gr_def(ctx, 0x407000 + (i<<8), 0x7b74f821);
+                       gr_def(ctx, 0x407004 + (i<<8), 0x89058001);
+
+                       if (dev_priv->chipset == 0x50) {
+                               cp_ctx(ctx, 0x407010 + (i<<8), 1);
+                       } else if (dev_priv->chipset < 0xa0) {
+                               cp_ctx(ctx, 0x407010 + (i<<8), 2);
+                               gr_def(ctx, 0x407010 + (i<<8), 0x00001000);
+                               gr_def(ctx, 0x407014 + (i<<8), 0x0000001f);
+                       } else {
+                               cp_ctx(ctx, 0x407010 + (i<<8), 3);
+                               gr_def(ctx, 0x407010 + (i<<8), 0x00001000);
+                               if (dev_priv->chipset != 0xa5)
+                                       gr_def(ctx, 0x407014 + (i<<8), 0x000000ff);
+                               else
+                                       gr_def(ctx, 0x407014 + (i<<8), 0x000001ff);
+                       }
+
+                       cp_ctx(ctx, 0x407080 + (i<<8), 4);
+                       if (dev_priv->chipset != 0xa5)
+                               gr_def(ctx, 0x407080 + (i<<8), 0x027c10fa);
+                       else
+                               gr_def(ctx, 0x407080 + (i<<8), 0x827c10fa);
+                       if (dev_priv->chipset == 0x50)
+                               gr_def(ctx, 0x407084 + (i<<8), 0x000000c0);
+                       else
+                               gr_def(ctx, 0x407084 + (i<<8), 0x400000c0);
+                       gr_def(ctx, 0x407088 + (i<<8), 0xb7892080);
+
+                       if (dev_priv->chipset < 0xa0)
+                               cp_ctx(ctx, 0x407094 + (i<<8), 1);
+                       else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
+                               cp_ctx(ctx, 0x407094 + (i<<8), 3);
+                       else {
+                               cp_ctx(ctx, 0x407094 + (i<<8), 4);
+                               gr_def(ctx, 0x4070a0 + (i<<8), 1);
+                       }
+               }
+       }
+
+       cp_ctx(ctx, 0x407c00, 0x3);
+       if (dev_priv->chipset < 0x90)
+               gr_def(ctx, 0x407c00, 0x00010040);
+       else if (dev_priv->chipset < 0xa0)
+               gr_def(ctx, 0x407c00, 0x00390040);
+       else
+               gr_def(ctx, 0x407c00, 0x003d0040);
+       gr_def(ctx, 0x407c08, 0x00000022);
+       if (dev_priv->chipset >= 0xa0) {
+               cp_ctx(ctx, 0x407c10, 0x3);
+               cp_ctx(ctx, 0x407c20, 0x1);
+               cp_ctx(ctx, 0x407c2c, 0x1);
+       }
+
+       if (dev_priv->chipset < 0xa0) {
+               cp_ctx(ctx, 0x407d00, 0x9);
+       } else {
+               cp_ctx(ctx, 0x407d00, 0x15);
+       }
+       if (dev_priv->chipset == 0x98)
+               gr_def(ctx, 0x407d08, 0x00380040);
+       else {
+               if (dev_priv->chipset < 0x90)
+                       gr_def(ctx, 0x407d08, 0x00010040);
+               else if (dev_priv->chipset < 0xa0)
+                       gr_def(ctx, 0x407d08, 0x00390040);
+               else
+                       gr_def(ctx, 0x407d08, 0x003d0040);
+               gr_def(ctx, 0x407d0c, 0x00000022);
+       }
+
+       /* 8000+: per-TP state */
+       for (i = 0; i < 10; i++) {
+               if (units & (1<<i)) {
+                       if (dev_priv->chipset < 0xa0)
+                               base = 0x408000 + (i<<12);
+                       else
+                               base = 0x408000 + (i<<11);
+                       if (dev_priv->chipset < 0xa0)
+                               offset = base + 0xc00;
+                       else
+                               offset = base + 0x80;
+                       cp_ctx(ctx, offset + 0x00, 1);
+                       gr_def(ctx, offset + 0x00, 0x0000ff0a);
+                       cp_ctx(ctx, offset + 0x08, 1);
+
+                       /* per-MP state */
+                       for (j = 0; j < (dev_priv->chipset < 0xa0 ? 2 : 4); j++) {
+                               if (!(units & (1 << (j+24)))) continue;
+                               if (dev_priv->chipset < 0xa0)
+                                       offset = base + 0x200 + (j<<7);
+                               else
+                                       offset = base + 0x100 + (j<<7);
+                               cp_ctx(ctx, offset, 0x20);
+                               gr_def(ctx, offset + 0x00, 0x01800000);
+                               gr_def(ctx, offset + 0x04, 0x00160000);
+                               gr_def(ctx, offset + 0x08, 0x01800000);
+                               gr_def(ctx, offset + 0x18, 0x0003ffff);
+                               switch (dev_priv->chipset) {
+                               case 0x50:
+                                       gr_def(ctx, offset + 0x1c, 0x00080000);
+                                       break;
+                               case 0x84:
+                                       gr_def(ctx, offset + 0x1c, 0x00880000);
+                                       break;
+                               case 0x86:
+                                       gr_def(ctx, offset + 0x1c, 0x008c0000);
+                                       break;
+                               case 0x92:
+                               case 0x96:
+                               case 0x98:
+                                       gr_def(ctx, offset + 0x1c, 0x118c0000);
+                                       break;
+                               case 0x94:
+                                       gr_def(ctx, offset + 0x1c, 0x10880000);
+                                       break;
+                               case 0xa0:
+                               case 0xa5:
+                                       gr_def(ctx, offset + 0x1c, 0x310c0000);
+                                       break;
+                               case 0xa8:
+                               case 0xaa:
+                               case 0xac:
+                                       gr_def(ctx, offset + 0x1c, 0x300c0000);
+                                       break;
+                               }
+                               gr_def(ctx, offset + 0x40, 0x00010401);
+                               if (dev_priv->chipset == 0x50)
+                                       gr_def(ctx, offset + 0x48, 0x00000040);
+                               else
+                                       gr_def(ctx, offset + 0x48, 0x00000078);
+                               gr_def(ctx, offset + 0x50, 0x000000bf);
+                               gr_def(ctx, offset + 0x58, 0x00001210);
+                               if (dev_priv->chipset == 0x50)
+                                       gr_def(ctx, offset + 0x5c, 0x00000080);
+                               else
+                                       gr_def(ctx, offset + 0x5c, 0x08000080);
+                               if (dev_priv->chipset >= 0xa0)
+                                       gr_def(ctx, offset + 0x68, 0x0000003e);
+                       }
+
+                       if (dev_priv->chipset < 0xa0)
+                               cp_ctx(ctx, base + 0x300, 0x4);
+                       else
+                               cp_ctx(ctx, base + 0x300, 0x5);
+                       if (dev_priv->chipset == 0x50)
+                               gr_def(ctx, base + 0x304, 0x00007070);
+                       else if (dev_priv->chipset < 0xa0)
+                               gr_def(ctx, base + 0x304, 0x00027070);
+                       else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
+                               gr_def(ctx, base + 0x304, 0x01127070);
+                       else
+                               gr_def(ctx, base + 0x304, 0x05127070);
+
+                       if (dev_priv->chipset < 0xa0)
+                               cp_ctx(ctx, base + 0x318, 1);
+                       else
+                               cp_ctx(ctx, base + 0x320, 1);
+                       if (dev_priv->chipset == 0x50)
+                               gr_def(ctx, base + 0x318, 0x0003ffff);
+                       else if (dev_priv->chipset < 0xa0)
+                               gr_def(ctx, base + 0x318, 0x03ffffff);
+                       else
+                               gr_def(ctx, base + 0x320, 0x07ffffff);
+
+                       if (dev_priv->chipset < 0xa0)
+                               cp_ctx(ctx, base + 0x324, 5);
+                       else
+                               cp_ctx(ctx, base + 0x328, 4);
+
+                       if (dev_priv->chipset < 0xa0) {
+                               cp_ctx(ctx, base + 0x340, 9);
+                               offset = base + 0x340;
+                       } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
+                               cp_ctx(ctx, base + 0x33c, 0xb);
+                               offset = base + 0x344;
+                       } else {
+                               cp_ctx(ctx, base + 0x33c, 0xd);
+                               offset = base + 0x344;
+                       }
+                       gr_def(ctx, offset + 0x0, 0x00120407);
+                       gr_def(ctx, offset + 0x4, 0x05091507);
+                       if (dev_priv->chipset == 0x84)
+                               gr_def(ctx, offset + 0x8, 0x05100202);
+                       else
+                               gr_def(ctx, offset + 0x8, 0x05010202);
+                       gr_def(ctx, offset + 0xc, 0x00030201);
+
+                       cp_ctx(ctx, base + 0x400, 2);
+                       gr_def(ctx, base + 0x404, 0x00000040);
+                       cp_ctx(ctx, base + 0x40c, 2);
+                       gr_def(ctx, base + 0x40c, 0x0d0c0b0a);
+                       gr_def(ctx, base + 0x410, 0x00141210);
+
+                       if (dev_priv->chipset < 0xa0)
+                               offset = base + 0x800;
+                       else
+                               offset = base + 0x500;
+                       cp_ctx(ctx, offset, 6);
+                       gr_def(ctx, offset + 0x0, 0x000001f0);
+                       gr_def(ctx, offset + 0x4, 0x00000001);
+                       gr_def(ctx, offset + 0x8, 0x00000003);
+                       if (dev_priv->chipset == 0x50 || dev_priv->chipset >= 0xaa)
+                               gr_def(ctx, offset + 0xc, 0x00008000);
+                       gr_def(ctx, offset + 0x14, 0x00039e00);
+                       cp_ctx(ctx, offset + 0x1c, 2);
+                       if (dev_priv->chipset == 0x50)
+                               gr_def(ctx, offset + 0x1c, 0x00000040);
+                       else
+                               gr_def(ctx, offset + 0x1c, 0x00000100);
+                       gr_def(ctx, offset + 0x20, 0x00003800);
+
+                       if (dev_priv->chipset >= 0xa0) {
+                               cp_ctx(ctx, base + 0x54c, 2);
+                               if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
+                                       gr_def(ctx, base + 0x54c, 0x003fe006);
+                               else
+                                       gr_def(ctx, base + 0x54c, 0x003fe007);
+                               gr_def(ctx, base + 0x550, 0x003fe000);
+                       }
+
+                       if (dev_priv->chipset < 0xa0)
+                               offset = base + 0xa00;
+                       else
+                               offset = base + 0x680;
+                       cp_ctx(ctx, offset, 1);
+                       gr_def(ctx, offset, 0x00404040);
+
+                       if (dev_priv->chipset < 0xa0)
+                               offset = base + 0xe00;
+                       else
+                               offset = base + 0x700;
+                       cp_ctx(ctx, offset, 2);
+                       if (dev_priv->chipset < 0xa0)
+                               gr_def(ctx, offset, 0x0077f005);
+                       else if (dev_priv->chipset == 0xa5)
+                               gr_def(ctx, offset, 0x6cf7f007);
+                       else if (dev_priv->chipset == 0xa8)
+                               gr_def(ctx, offset, 0x6cfff007);
+                       else if (dev_priv->chipset == 0xac)
+                               gr_def(ctx, offset, 0x0cfff007);
+                       else
+                               gr_def(ctx, offset, 0x0cf7f007);
+                       if (dev_priv->chipset == 0x50)
+                               gr_def(ctx, offset + 0x4, 0x00007fff);
+                       else if (dev_priv->chipset < 0xa0)
+                               gr_def(ctx, offset + 0x4, 0x003f7fff);
+                       else
+                               gr_def(ctx, offset + 0x4, 0x02bf7fff);
+                       cp_ctx(ctx, offset + 0x2c, 1);
+                       if (dev_priv->chipset == 0x50) {
+                               cp_ctx(ctx, offset + 0x50, 9);
+                               gr_def(ctx, offset + 0x54, 0x000003ff);
+                               gr_def(ctx, offset + 0x58, 0x00000003);
+                               gr_def(ctx, offset + 0x5c, 0x00000003);
+                               gr_def(ctx, offset + 0x60, 0x000001ff);
+                               gr_def(ctx, offset + 0x64, 0x0000001f);
+                               gr_def(ctx, offset + 0x68, 0x0000000f);
+                               gr_def(ctx, offset + 0x6c, 0x0000000f);
+                       } else if(dev_priv->chipset < 0xa0) {
+                               cp_ctx(ctx, offset + 0x50, 1);
+                               cp_ctx(ctx, offset + 0x70, 1);
+                       } else {
+                               cp_ctx(ctx, offset + 0x50, 1);
+                               cp_ctx(ctx, offset + 0x60, 5);
+                       }
+               }
+       }
+}
+
+/*
+ * xfer areas. These are a pain.
+ *
+ * There are 2 xfer areas: the first one is big and contains all sorts of
+ * stuff, the second is small and contains some per-TP context.
+ *
+ * Each area is split into 8 "strands". The areas, when saved to grctx,
+ * are made of 8-word blocks. Each block contains a single word from
+ * each strand. The strands are independent of each other, their
+ * addresses are unrelated to each other, and data in them is closely
+ * packed together. The strand layout varies a bit between cards: here
+ * and there, a single word is thrown out in the middle and the whole
+ * strand is offset by a bit from corresponding one on another chipset.
+ * For this reason, addresses of stuff in strands are almost useless.
+ * Knowing sequence of stuff and size of gaps between them is much more
+ * useful, and that's how we build the strands in our generator.
+ *
+ * NVA0 takes this mess to a whole new level by cutting the old strands
+ * into a few dozen pieces [known as genes], rearranging them randomly,
+ * and putting them back together to make new strands. Hopefully these
+ * genes correspond more or less directly to the same PGRAPH subunits
+ * as in 400040 register.
+ *
+ * The most common value in default context is 0, and when the genes
+ * are separated by 0's, gene bounduaries are quite speculative...
+ * some of them can be clearly deduced, others can be guessed, and yet
+ * others won't be resolved without figuring out the real meaning of
+ * given ctxval. For the same reason, ending point of each strand
+ * is unknown. Except for strand 0, which is the longest strand and
+ * its end corresponds to end of the whole xfer.
+ *
+ * An unsolved mystery is the seek instruction: it takes an argument
+ * in bits 8-18, and that argument is clearly the place in strands to
+ * seek to... but the offsets don't seem to correspond to offsets as
+ * seen in grctx. Perhaps there's another, real, not randomly-changing
+ * addressing in strands, and the xfer insn just happens to skip over
+ * the unused bits? NV10-NV30 PIPE comes to mind...
+ *
+ * As far as I know, there's no way to access the xfer areas directly
+ * without the help of ctxprog.
+ */
+
+static inline void
+xf_emit(struct nouveau_grctx *ctx, int num, uint32_t val) {
+       int i;
+       if (val && ctx->mode == NOUVEAU_GRCTX_VALS)
+               for (i = 0; i < num; i++)
+                       nv_wo32(ctx->dev, ctx->data, ctx->ctxvals_pos + (i << 3), val);
+       ctx->ctxvals_pos += num << 3;
+}
+
+/* Gene declarations... */
+
+static void nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx);
+static void nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx);
+
+static void
+nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int i;
+       int offset;
+       int size = 0;
+       uint32_t units = nv_rd32 (ctx->dev, 0x1540);
+
+       offset = (ctx->ctxvals_pos+0x3f)&~0x3f;
+       ctx->ctxvals_base = offset;
+
+       if (dev_priv->chipset < 0xa0) {
+               /* Strand 0 */
+               ctx->ctxvals_pos = offset;
+               switch (dev_priv->chipset) {
+               case 0x50:
+                       xf_emit(ctx, 0x99, 0);
+                       break;
+               case 0x84:
+               case 0x86:
+                       xf_emit(ctx, 0x384, 0);
+                       break;
+               case 0x92:
+               case 0x94:
+               case 0x96:
+               case 0x98:
+                       xf_emit(ctx, 0x380, 0);
+                       break;
+               }
+               nv50_graph_construct_gene_m2mf (ctx);
+               switch (dev_priv->chipset) {
+               case 0x50:
+               case 0x84:
+               case 0x86:
+               case 0x98:
+                       xf_emit(ctx, 0x4c4, 0);
+                       break;
+               case 0x92:
+               case 0x94:
+               case 0x96:
+                       xf_emit(ctx, 0x984, 0);
+                       break;
+               }
+               nv50_graph_construct_gene_unk5(ctx);
+               if (dev_priv->chipset == 0x50)
+                       xf_emit(ctx, 0xa, 0);
+               else
+                       xf_emit(ctx, 0xb, 0);
+               nv50_graph_construct_gene_unk4(ctx);
+               nv50_graph_construct_gene_unk3(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 1 */
+               ctx->ctxvals_pos = offset + 0x1;
+               nv50_graph_construct_gene_unk6(ctx);
+               nv50_graph_construct_gene_unk7(ctx);
+               nv50_graph_construct_gene_unk8(ctx);
+               switch (dev_priv->chipset) {
+               case 0x50:
+               case 0x92:
+                       xf_emit(ctx, 0xfb, 0);
+                       break;
+               case 0x84:
+                       xf_emit(ctx, 0xd3, 0);
+                       break;
+               case 0x94:
+               case 0x96:
+                       xf_emit(ctx, 0xab, 0);
+                       break;
+               case 0x86:
+               case 0x98:
+                       xf_emit(ctx, 0x6b, 0);
+                       break;
+               }
+               xf_emit(ctx, 2, 0x4e3bfdf);
+               xf_emit(ctx, 4, 0);
+               xf_emit(ctx, 1, 0x0fac6881);
+               xf_emit(ctx, 0xb, 0);
+               xf_emit(ctx, 2, 0x4e3bfdf);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 2 */
+               ctx->ctxvals_pos = offset + 0x2;
+               switch (dev_priv->chipset) {
+               case 0x50:
+               case 0x92:
+                       xf_emit(ctx, 0xa80, 0);
+                       break;
+               case 0x84:
+                       xf_emit(ctx, 0xa7e, 0);
+                       break;
+               case 0x94:
+               case 0x96:
+                       xf_emit(ctx, 0xa7c, 0);
+                       break;
+               case 0x86:
+               case 0x98:
+                       xf_emit(ctx, 0xa7a, 0);
+                       break;
+               }
+               xf_emit(ctx, 1, 0x3fffff);
+               xf_emit(ctx, 2, 0);
+               xf_emit(ctx, 1, 0x1fff);
+               xf_emit(ctx, 0xe, 0);
+               nv50_graph_construct_gene_unk9(ctx);
+               nv50_graph_construct_gene_unk2(ctx);
+               nv50_graph_construct_gene_unk1(ctx);
+               nv50_graph_construct_gene_unk10(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 3: per-ROP group state */
+               ctx->ctxvals_pos = offset + 3;
+               for (i = 0; i < 6; i++)
+                       if (units & (1 << (i + 16)))
+                               nv50_graph_construct_gene_ropc(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strands 4-7: per-TP state */
+               for (i = 0; i < 4; i++) {
+                       ctx->ctxvals_pos = offset + 4 + i;
+                       if (units & (1 << (2 * i)))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if (units & (1 << (2 * i + 1)))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if ((ctx->ctxvals_pos-offset)/8 > size)
+                               size = (ctx->ctxvals_pos-offset)/8;
+               }
+       } else {
+               /* Strand 0 */
+               ctx->ctxvals_pos = offset;
+               if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+                       xf_emit(ctx, 0x385, 0);
+               else
+                       xf_emit(ctx, 0x384, 0);
+               nv50_graph_construct_gene_m2mf(ctx);
+               xf_emit(ctx, 0x950, 0);
+               nv50_graph_construct_gene_unk10(ctx);
+               xf_emit(ctx, 1, 0x0fac6881);
+               if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+                       xf_emit(ctx, 1, 1);
+                       xf_emit(ctx, 3, 0);
+               }
+               nv50_graph_construct_gene_unk8(ctx);
+               if (dev_priv->chipset == 0xa0)
+                       xf_emit(ctx, 0x189, 0);
+               else if (dev_priv->chipset < 0xa8)
+                       xf_emit(ctx, 0x99, 0);
+               else if (dev_priv->chipset == 0xaa)
+                       xf_emit(ctx, 0x65, 0);
+               else
+                       xf_emit(ctx, 0x6d, 0);
+               nv50_graph_construct_gene_unk9(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 1 */
+               ctx->ctxvals_pos = offset + 1;
+               nv50_graph_construct_gene_unk1(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 2 */
+               ctx->ctxvals_pos = offset + 2;
+               if (dev_priv->chipset == 0xa0) {
+                       nv50_graph_construct_gene_unk2(ctx);
+               }
+               xf_emit(ctx, 0x36, 0);
+               nv50_graph_construct_gene_unk5(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 3 */
+               ctx->ctxvals_pos = offset + 3;
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 1, 1);
+               nv50_graph_construct_gene_unk6(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 4 */
+               ctx->ctxvals_pos = offset + 4;
+               if (dev_priv->chipset == 0xa0)
+                       xf_emit(ctx, 0xa80, 0);
+               else
+                       xf_emit(ctx, 0xa7a, 0);
+               xf_emit(ctx, 1, 0x3fffff);
+               xf_emit(ctx, 2, 0);
+               xf_emit(ctx, 1, 0x1fff);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 5 */
+               ctx->ctxvals_pos = offset + 5;
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 1, 0x0fac6881);
+               xf_emit(ctx, 0xb, 0);
+               xf_emit(ctx, 2, 0x4e3bfdf);
+               xf_emit(ctx, 3, 0);
+               if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+                       xf_emit(ctx, 1, 0x11);
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 2, 0x4e3bfdf);
+               xf_emit(ctx, 2, 0);
+               if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+                       xf_emit(ctx, 1, 0x11);
+               xf_emit(ctx, 1, 0);
+               for (i = 0; i < 8; i++)
+                       if (units & (1<<(i+16)))
+                               nv50_graph_construct_gene_ropc(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 6 */
+               ctx->ctxvals_pos = offset + 6;
+               nv50_graph_construct_gene_unk3(ctx);
+               xf_emit(ctx, 0xb, 0);
+               nv50_graph_construct_gene_unk4(ctx);
+               nv50_graph_construct_gene_unk7(ctx);
+               if (units & (1 << 0))
+                       nv50_graph_construct_xfer_tp(ctx);
+               if (units & (1 << 1))
+                       nv50_graph_construct_xfer_tp(ctx);
+               if (units & (1 << 2))
+                       nv50_graph_construct_xfer_tp(ctx);
+               if (units & (1 << 3))
+                       nv50_graph_construct_xfer_tp(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 7 */
+               ctx->ctxvals_pos = offset + 7;
+               if (dev_priv->chipset == 0xa0) {
+                       if (units & (1 << 4))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if (units & (1 << 5))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if (units & (1 << 6))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if (units & (1 << 7))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if (units & (1 << 8))
+                               nv50_graph_construct_xfer_tp(ctx);
+                       if (units & (1 << 9))
+                               nv50_graph_construct_xfer_tp(ctx);
+               } else {
+                       nv50_graph_construct_gene_unk2(ctx);
+               }
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+       }
+
+       ctx->ctxvals_pos = offset + size * 8;
+       ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f;
+       cp_lsr (ctx, offset);
+       cp_out (ctx, CP_SET_XFER_POINTER);
+       cp_lsr (ctx, size);
+       cp_out (ctx, CP_SEEK_1);
+       cp_out (ctx, CP_XFER_1);
+       cp_wait(ctx, XFER, BUSY);
+}
+
+/*
+ * non-trivial demagiced parts of ctx init go here
+ */
+
+static void
+nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx)
+{
+       /* m2mf state */
+       xf_emit (ctx, 1, 0);            /* DMA_NOTIFY instance >> 4 */
+       xf_emit (ctx, 1, 0);            /* DMA_BUFFER_IN instance >> 4 */
+       xf_emit (ctx, 1, 0);            /* DMA_BUFFER_OUT instance >> 4 */
+       xf_emit (ctx, 1, 0);            /* OFFSET_IN */
+       xf_emit (ctx, 1, 0);            /* OFFSET_OUT */
+       xf_emit (ctx, 1, 0);            /* PITCH_IN */
+       xf_emit (ctx, 1, 0);            /* PITCH_OUT */
+       xf_emit (ctx, 1, 0);            /* LINE_LENGTH */
+       xf_emit (ctx, 1, 0);            /* LINE_COUNT */
+       xf_emit (ctx, 1, 0x21);         /* FORMAT: bits 0-4 INPUT_INC, bits 5-9 OUTPUT_INC */
+       xf_emit (ctx, 1, 1);            /* LINEAR_IN */
+       xf_emit (ctx, 1, 0x2);          /* TILING_MODE_IN: bits 0-2 y tiling, bits 3-5 z tiling */
+       xf_emit (ctx, 1, 0x100);        /* TILING_PITCH_IN */
+       xf_emit (ctx, 1, 0x100);        /* TILING_HEIGHT_IN */
+       xf_emit (ctx, 1, 1);            /* TILING_DEPTH_IN */
+       xf_emit (ctx, 1, 0);            /* TILING_POSITION_IN_Z */
+       xf_emit (ctx, 1, 0);            /* TILING_POSITION_IN */
+       xf_emit (ctx, 1, 1);            /* LINEAR_OUT */
+       xf_emit (ctx, 1, 0x2);          /* TILING_MODE_OUT: bits 0-2 y tiling, bits 3-5 z tiling */
+       xf_emit (ctx, 1, 0x100);        /* TILING_PITCH_OUT */
+       xf_emit (ctx, 1, 0x100);        /* TILING_HEIGHT_OUT */
+       xf_emit (ctx, 1, 1);            /* TILING_DEPTH_OUT */
+       xf_emit (ctx, 1, 0);            /* TILING_POSITION_OUT_Z */
+       xf_emit (ctx, 1, 0);            /* TILING_POSITION_OUT */
+       xf_emit (ctx, 1, 0);            /* OFFSET_IN_HIGH */
+       xf_emit (ctx, 1, 0);            /* OFFSET_OUT_HIGH */
+}
+
+static void
+nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* end of area 2 on pre-NVA0, area 1 on NVAx */
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x80);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0x80c14);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 1, 0x3ff);
+       else
+               xf_emit(ctx, 1, 0x7ff);
+       switch (dev_priv->chipset) {
+       case 0x50:
+       case 0x86:
+       case 0x98:
+       case 0xaa:
+       case 0xac:
+               xf_emit(ctx, 0x542, 0);
+               break;
+       case 0x84:
+       case 0x92:
+       case 0x94:
+       case 0x96:
+               xf_emit(ctx, 0x942, 0);
+               break;
+       case 0xa0:
+               xf_emit(ctx, 0x2042, 0);
+               break;
+       case 0xa5:
+       case 0xa8:
+               xf_emit(ctx, 0x842, 0);
+               break;
+       }
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x80);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x27);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x26);
+       xf_emit(ctx, 3, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx)
+{
+       /* end of area 2 on pre-NVA0, area 1 on NVAx */
+       xf_emit(ctx, 0x10, 0x04000000);
+       xf_emit(ctx, 0x24, 0);
+       xf_emit(ctx, 2, 0x04e3bfdf);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x1fe21);
+}
+
+static void
+nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* middle of area 2 on pre-NVA0, beginning of area 2 on NVA0, area 7 on >NVA0 */
+       if (dev_priv->chipset != 0x50) {
+               xf_emit(ctx, 5, 0);
+               xf_emit(ctx, 1, 0x80c14);
+               xf_emit(ctx, 2, 0);
+               xf_emit(ctx, 1, 0x804);
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 2, 4);
+               xf_emit(ctx, 1, 0x8100c12);
+       }
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x10);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 3, 0);
+       else
+               xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0x804);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x1a);
+       if (dev_priv->chipset != 0x50)
+               xf_emit(ctx, 1, 0x7f);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x80c14);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 6, 0);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 1, 0x3ff);
+       else
+               xf_emit(ctx, 1, 0x7ff);
+       xf_emit(ctx, 1, 0x80c14);
+       xf_emit(ctx, 0x38, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 0x38, 0);
+       xf_emit(ctx, 2, 0x88);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 0x16, 0);
+       xf_emit(ctx, 1, 0x26);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x3f800000);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 4, 0);
+       else
+               xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x1a);
+       xf_emit(ctx, 1, 0x10);
+       if (dev_priv->chipset != 0x50)
+               xf_emit(ctx, 0x28, 0);
+       else
+               xf_emit(ctx, 0x25, 0);
+       xf_emit(ctx, 1, 0x52);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x26);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x1a);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x00ffff00);
+       xf_emit(ctx, 1, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* end of area 0 on pre-NVA0, beginning of area 6 on NVAx */
+       xf_emit(ctx, 1, 0x3f);
+       xf_emit(ctx, 0xa, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 0x04000000);
+       xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 4);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 0x10, 0);
+       else
+               xf_emit(ctx, 0x11, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x1001);
+       xf_emit(ctx, 4, 0xffff);
+       xf_emit(ctx, 0x20, 0);
+       xf_emit(ctx, 0x10, 0x3f800000);
+       xf_emit(ctx, 1, 0x10);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 1, 0);
+       else
+               xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 3);
+       xf_emit(ctx, 2, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx)
+{
+       /* middle of area 0 on pre-NVA0, middle of area 6 on NVAx */
+       xf_emit(ctx, 2, 0x04000000);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x80);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x80);
+       xf_emit(ctx, 1, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* middle of area 0 on pre-NVA0 [after m2mf], end of area 2 on NVAx */
+       xf_emit(ctx, 2, 4);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0x1c4d, 0);
+       else
+               xf_emit(ctx, 0x1c4b, 0);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0x8100c12);
+       if (dev_priv->chipset != 0x50)
+               xf_emit(ctx, 1, 3);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x80c14);
+       xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0x80c14);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 1, 0x27);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x3c1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x16, 0);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 1, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* beginning of area 1 on pre-NVA0 [after m2mf], area 3 on NVAx */
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0xf);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 8, 0);
+       else
+               xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0x20);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0x11, 0);
+       else if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 0xf, 0);
+       else
+               xf_emit(ctx, 0xe, 0);
+       xf_emit(ctx, 1, 0x1a);
+       xf_emit(ctx, 0xd, 0);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 8);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 1, 0x3ff);
+       else
+               xf_emit(ctx, 1, 0x7ff);
+       if (dev_priv->chipset == 0xa8)
+               xf_emit(ctx, 1, 0x1e00);
+       xf_emit(ctx, 0xc, 0);
+       xf_emit(ctx, 1, 0xf);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 0x125, 0);
+       else if (dev_priv->chipset < 0xa0)
+               xf_emit(ctx, 0x126, 0);
+       else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa)
+               xf_emit(ctx, 0x124, 0);
+       else
+               xf_emit(ctx, 0x1f7, 0);
+       xf_emit(ctx, 1, 0xf);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 3, 0);
+       else
+               xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0xa1, 0);
+       else
+               xf_emit(ctx, 0x5a, 0);
+       xf_emit(ctx, 1, 0xf);
+       if (dev_priv->chipset < 0xa0)
+               xf_emit(ctx, 0x834, 0);
+       else if (dev_priv->chipset == 0xa0)
+               xf_emit(ctx, 0x1873, 0);
+       else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0x8ba, 0);
+       else
+               xf_emit(ctx, 0x833, 0);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 0xf, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 6 on NVAx */
+       xf_emit(ctx, 2, 0);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 2, 1);
+       else
+               xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0x100);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 8);
+       xf_emit(ctx, 5, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 3, 1);
+       xf_emit(ctx, 1, 0xcf);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 6, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 3, 1);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x15);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x4444480);
+       xf_emit(ctx, 0x37, 0);
+}
+
+static void
+nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx)
+{
+       /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 0 on NVAx */
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0x100);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x10001);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x10001);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x10001);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 2);
+}
+
+static void
+nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       /* middle of area 2 on pre-NVA0 [after m2mf], end of area 0 on NVAx */
+       xf_emit(ctx, 1, 0x3f800000);
+       xf_emit(ctx, 6, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0x1a);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x12, 0);
+       xf_emit(ctx, 1, 0x00ffff00);
+       xf_emit(ctx, 6, 0);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 0xf, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 2, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 3);
+       else if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 0x04000000);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 5);
+       xf_emit(ctx, 1, 0x52);
+       if (dev_priv->chipset == 0x50) {
+               xf_emit(ctx, 0x13, 0);
+       } else {
+               xf_emit(ctx, 4, 0);
+               xf_emit(ctx, 1, 1);
+               if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+                       xf_emit(ctx, 0x11, 0);
+               else
+                       xf_emit(ctx, 0x10, 0);
+       }
+       xf_emit(ctx, 0x10, 0x3f800000);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 0x26, 0);
+       xf_emit(ctx, 1, 0x8100c12);
+       xf_emit(ctx, 1, 5);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 4, 0xffff);
+       if (dev_priv->chipset != 0x50)
+               xf_emit(ctx, 1, 3);
+       if (dev_priv->chipset < 0xa0)
+               xf_emit(ctx, 0x1f, 0);
+       else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0xc, 0);
+       else
+               xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x00ffff00);
+       xf_emit(ctx, 1, 0x1a);
+       if (dev_priv->chipset != 0x50) {
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 1, 3);
+       }
+       if (dev_priv->chipset < 0xa0)
+               xf_emit(ctx, 0x26, 0);
+       else
+               xf_emit(ctx, 0x3c, 0);
+       xf_emit(ctx, 1, 0x102);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 4, 4);
+       if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 1, 0x3ff);
+       else
+               xf_emit(ctx, 1, 0x7ff);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x102);
+       xf_emit(ctx, 9, 0);
+       xf_emit(ctx, 4, 4);
+       xf_emit(ctx, 0x2c, 0);
+}
+
+static void
+nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int magic2;
+       if (dev_priv->chipset == 0x50) {
+               magic2 = 0x00003e60;
+       } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
+               magic2 = 0x001ffe67;
+       } else {
+               magic2 = 0x00087e67;
+       }
+       xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, magic2);
+       xf_emit(ctx, 4, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 7, 0);
+       if (dev_priv->chipset >= 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 0x15);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 4, 0);
+       if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x92 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa0) {
+               xf_emit(ctx, 1, 4);
+               xf_emit(ctx, 1, 0x400);
+               xf_emit(ctx, 1, 0x300);
+               xf_emit(ctx, 1, 0x1001);
+               if (dev_priv->chipset != 0xa0) {
+                       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+                               xf_emit(ctx, 1, 0);
+                       else
+                               xf_emit(ctx, 1, 0x15);
+               }
+               xf_emit(ctx, 3, 0);
+       }
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x13, 0);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 0x10, 0);
+       xf_emit(ctx, 0x10, 0x3f800000);
+       xf_emit(ctx, 0x19, 0);
+       xf_emit(ctx, 1, 0x10);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x3f);
+       xf_emit(ctx, 6, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset >= 0xa0) {
+               xf_emit(ctx, 2, 0);
+               xf_emit(ctx, 1, 0x1001);
+               xf_emit(ctx, 0xb, 0);
+       } else {
+               xf_emit(ctx, 0xc, 0);
+       }
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x11);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 4, 0);
+       else
+               xf_emit(ctx, 6, 0);
+       xf_emit(ctx, 3, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, magic2);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 0x18, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 8, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 8, 1);
+               xf_emit(ctx, 3, 0);
+               xf_emit(ctx, 1, 1);
+               xf_emit(ctx, 5, 0);
+               xf_emit(ctx, 1, 1);
+               xf_emit(ctx, 0x16, 0);
+       } else {
+               if (dev_priv->chipset >= 0xa0)
+                       xf_emit(ctx, 0x1b, 0);
+               else
+                       xf_emit(ctx, 0x15, 0);
+       }
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 1);
+       if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 4, 0);
+       else
+               xf_emit(ctx, 3, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 0x10, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 0x10, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 8, 1);
+               xf_emit(ctx, 3, 0);
+       }
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x5b, 0);
+}
+
+static void
+nv50_graph_construct_xfer_tp_x1(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int magic3;
+       if (dev_priv->chipset == 0x50)
+               magic3 = 0x1000;
+       else if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8)
+               magic3 = 0x1e00;
+       else
+               magic3 = 0;
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 4);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0x24, 0);
+       else if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 0x14, 0);
+       else
+               xf_emit(ctx, 0x15, 0);
+       xf_emit(ctx, 2, 4);
+       if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 1, 0x03020100);
+       else
+               xf_emit(ctx, 1, 0x00608080);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 2, 4);
+       xf_emit(ctx, 1, 0x80);
+       if (magic3)
+               xf_emit(ctx, 1, magic3);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 0x24, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0x80);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0x03020100);
+       xf_emit(ctx, 1, 3);
+       if (magic3)
+               xf_emit(ctx, 1, magic3);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 3);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 4);
+       if (dev_priv->chipset == 0x94 || dev_priv->chipset == 0x96)
+               xf_emit(ctx, 0x1024, 0);
+       else if (dev_priv->chipset < 0xa0)
+               xf_emit(ctx, 0xa24, 0);
+       else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa)
+               xf_emit(ctx, 0x214, 0);
+       else
+               xf_emit(ctx, 0x414, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 3);
+       xf_emit(ctx, 2, 0);
+}
+
+static void
+nv50_graph_construct_xfer_tp_x2(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int magic1, magic2;
+       if (dev_priv->chipset == 0x50) {
+               magic1 = 0x3ff;
+               magic2 = 0x00003e60;
+       } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
+               magic1 = 0x7ff;
+               magic2 = 0x001ffe67;
+       } else {
+               magic1 = 0x7ff;
+               magic2 = 0x00087e67;
+       }
+       xf_emit(ctx, 3, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0xc, 0);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 0xb, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 4, 0xffff);
+       xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 5, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 1, 3);
+               xf_emit(ctx, 1, 0);
+       } else if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0xa, 0);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 1, 2);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 0x18, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 8, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 8, 1);
+               xf_emit(ctx, 1, 0);
+       }
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 3, 0xcf);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0xa, 0);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 8, 1);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, magic2);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x11);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 2, 1);
+       else
+               xf_emit(ctx, 1, 1);
+       if(dev_priv->chipset == 0x50)
+               xf_emit(ctx, 1, 0);
+       else
+               xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 5, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, magic1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x28, 0);
+       xf_emit(ctx, 8, 8);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 8, 0x400);
+       xf_emit(ctx, 8, 0x300);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x20);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 0x100);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x40);
+       xf_emit(ctx, 1, 0x100);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 3);
+       xf_emit(ctx, 4, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, magic2);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 9, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x400);
+       xf_emit(ctx, 1, 0x300);
+       xf_emit(ctx, 1, 0x1001);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 4, 0);
+       else
+               xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 1, 0xf);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 0x15, 0);
+               xf_emit(ctx, 1, 1);
+               xf_emit(ctx, 3, 0);
+       } else
+               xf_emit(ctx, 0x17, 0);
+       if (dev_priv->chipset >= 0xa0)
+               xf_emit(ctx, 1, 0x0fac6881);
+       xf_emit(ctx, 1, magic2);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 3, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 2, 1);
+       else
+               xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 2, 0);
+       else if (dev_priv->chipset != 0x50)
+               xf_emit(ctx, 1, 0);
+}
+
+static void
+nv50_graph_construct_xfer_tp_x3(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 2, 0);
+       else
+               xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0x2a712488);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x4085c000);
+       xf_emit(ctx, 1, 0x40);
+       xf_emit(ctx, 1, 0x100);
+       xf_emit(ctx, 1, 0x10100);
+       xf_emit(ctx, 1, 0x02800000);
+}
+
+static void
+nv50_graph_construct_xfer_tp_x4(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       xf_emit(ctx, 2, 0x04e3bfdf);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x00ffff00);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 2, 1);
+       else
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 0x00ffff00);
+       xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0x30201000);
+       xf_emit(ctx, 1, 0x70605040);
+       xf_emit(ctx, 1, 0xb8a89888);
+       xf_emit(ctx, 1, 0xf8e8d8c8);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x1a);
+}
+
+static void
+nv50_graph_construct_xfer_tp_x5(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 0xfac6881);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 2, 0);
+       xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 0xb, 0);
+       else
+               xf_emit(ctx, 0xa, 0);
+       xf_emit(ctx, 8, 1);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0xfac6881);
+       xf_emit(ctx, 1, 0xf);
+       xf_emit(ctx, 7, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 1);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 6, 0);
+               xf_emit(ctx, 1, 1);
+               xf_emit(ctx, 6, 0);
+       } else {
+               xf_emit(ctx, 0xb, 0);
+       }
+}
+
+static void
+nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       if (dev_priv->chipset < 0xa0) {
+               nv50_graph_construct_xfer_tp_x1(ctx);
+               nv50_graph_construct_xfer_tp_x2(ctx);
+               nv50_graph_construct_xfer_tp_x3(ctx);
+               if (dev_priv->chipset == 0x50)
+                       xf_emit(ctx, 0xf, 0);
+               else
+                       xf_emit(ctx, 0x12, 0);
+               nv50_graph_construct_xfer_tp_x4(ctx);
+       } else {
+               nv50_graph_construct_xfer_tp_x3(ctx);
+               if (dev_priv->chipset < 0xaa)
+                       xf_emit(ctx, 0xc, 0);
+               else
+                       xf_emit(ctx, 0xa, 0);
+               nv50_graph_construct_xfer_tp_x2(ctx);
+               nv50_graph_construct_xfer_tp_x5(ctx);
+               nv50_graph_construct_xfer_tp_x4(ctx);
+               nv50_graph_construct_xfer_tp_x1(ctx);
+       }
+}
+
+static void
+nv50_graph_construct_xfer_tp2(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int i, mpcnt;
+       if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa)
+               mpcnt = 1;
+       else if (dev_priv->chipset < 0xa0 || dev_priv->chipset >= 0xa8)
+               mpcnt = 2;
+       else
+               mpcnt = 3;
+       for (i = 0; i < mpcnt; i++) {
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 1, 0x80);
+               xf_emit(ctx, 1, 0x80007004);
+               xf_emit(ctx, 1, 0x04000400);
+               if (dev_priv->chipset >= 0xa0)
+                       xf_emit(ctx, 1, 0xc0);
+               xf_emit(ctx, 1, 0x1000);
+               xf_emit(ctx, 2, 0);
+               if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8) {
+                       xf_emit(ctx, 1, 0xe00);
+                       xf_emit(ctx, 1, 0x1e00);
+               }
+               xf_emit(ctx, 1, 1);
+               xf_emit(ctx, 2, 0);
+               if (dev_priv->chipset == 0x50)
+                       xf_emit(ctx, 2, 0x1000);
+               xf_emit(ctx, 1, 1);
+               xf_emit(ctx, 1, 0);
+               xf_emit(ctx, 1, 4);
+               xf_emit(ctx, 1, 2);
+               if (dev_priv->chipset >= 0xaa)
+                       xf_emit(ctx, 0xb, 0);
+               else if (dev_priv->chipset >= 0xa0)
+                       xf_emit(ctx, 0xc, 0);
+               else
+                       xf_emit(ctx, 0xa, 0);
+       }
+       xf_emit(ctx, 1, 0x08100c12);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset >= 0xa0) {
+               xf_emit(ctx, 1, 0x1fe21);
+       }
+       xf_emit(ctx, 5, 0);
+       xf_emit(ctx, 4, 0xffff);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 2, 0x10001);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 0x1fe21);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 4, 0);
+       xf_emit(ctx, 1, 0x08100c12);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 8, 0);
+       xf_emit(ctx, 1, 0xfac6881);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
+               xf_emit(ctx, 1, 3);
+       xf_emit(ctx, 3, 0);
+       xf_emit(ctx, 1, 4);
+       xf_emit(ctx, 9, 0);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 2, 1);
+       xf_emit(ctx, 1, 2);
+       xf_emit(ctx, 3, 1);
+       xf_emit(ctx, 1, 0);
+       if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 0x10, 1);
+               xf_emit(ctx, 8, 2);
+               xf_emit(ctx, 0x18, 1);
+               xf_emit(ctx, 3, 0);
+       }
+       xf_emit(ctx, 1, 4);
+       if (dev_priv->chipset == 0x50)
+               xf_emit(ctx, 0x3a0, 0);
+       else if (dev_priv->chipset < 0x94)
+               xf_emit(ctx, 0x3a2, 0);
+       else if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa)
+               xf_emit(ctx, 0x39f, 0);
+       else
+               xf_emit(ctx, 0x3a3, 0);
+       xf_emit(ctx, 1, 0x11);
+       xf_emit(ctx, 1, 0);
+       xf_emit(ctx, 1, 1);
+       xf_emit(ctx, 0x2d, 0);
+}
+
+static void
+nv50_graph_construct_xfer2(struct nouveau_grctx *ctx)
+{
+       struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
+       int i;
+       uint32_t offset;
+       uint32_t units = nv_rd32 (ctx->dev, 0x1540);
+       int size = 0;
+
+       offset = (ctx->ctxvals_pos+0x3f)&~0x3f;
+
+       if (dev_priv->chipset < 0xa0) {
+               for (i = 0; i < 8; i++) {
+                       ctx->ctxvals_pos = offset + i;
+                       if (i == 0)
+                               xf_emit(ctx, 1, 0x08100c12);
+                       if (units & (1 << i))
+                               nv50_graph_construct_xfer_tp2(ctx);
+                       if ((ctx->ctxvals_pos-offset)/8 > size)
+                               size = (ctx->ctxvals_pos-offset)/8;
+               }
+       } else {
+               /* Strand 0: TPs 0, 1 */
+               ctx->ctxvals_pos = offset;
+               xf_emit(ctx, 1, 0x08100c12);
+               if (units & (1 << 0))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if (units & (1 << 1))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 0: TPs 2, 3 */
+               ctx->ctxvals_pos = offset + 1;
+               if (units & (1 << 2))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if (units & (1 << 3))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 0: TPs 4, 5, 6 */
+               ctx->ctxvals_pos = offset + 2;
+               if (units & (1 << 4))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if (units & (1 << 5))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if (units & (1 << 6))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+
+               /* Strand 0: TPs 7, 8, 9 */
+               ctx->ctxvals_pos = offset + 3;
+               if (units & (1 << 7))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if (units & (1 << 8))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if (units & (1 << 9))
+                       nv50_graph_construct_xfer_tp2(ctx);
+               if ((ctx->ctxvals_pos-offset)/8 > size)
+                       size = (ctx->ctxvals_pos-offset)/8;
+       }
+       ctx->ctxvals_pos = offset + size * 8;
+       ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f;
+       cp_lsr (ctx, offset);
+       cp_out (ctx, CP_SET_XFER_POINTER);
+       cp_lsr (ctx, size);
+       cp_out (ctx, CP_SEEK_2);
+       cp_out (ctx, CP_XFER_2);
+       cp_wait(ctx, XFER, BUSY);
+}
index f0dc4e36ef055c7d711eaf1a54a501d41bbce2d2..de1f5b0062c551c3f9480aa0efeb326634f3f635 100644 (file)
@@ -390,7 +390,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
        if (gpuobj->im_backing)
                return -EINVAL;
 
-       *sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1);
+       *sz = ALIGN(*sz, NV50_INSTMEM_PAGE_SIZE);
        if (*sz == 0)
                return -EINVAL;
 
index 1cc7b937b1eaea4cdaac2df729613a6142fa9e90..ed38262d9985e326e4bdffceb0fca5ea865e3362 100644 (file)
@@ -30,6 +30,9 @@ $(obj)/r420_reg_safe.h: $(src)/reg_srcs/r420 $(obj)/mkregtable
 $(obj)/rs600_reg_safe.h: $(src)/reg_srcs/rs600 $(obj)/mkregtable
        $(call if_changed,mkregtable)
 
+$(obj)/r600_reg_safe.h: $(src)/reg_srcs/r600 $(obj)/mkregtable
+       $(call if_changed,mkregtable)
+
 $(obj)/r100.o: $(obj)/r100_reg_safe.h $(obj)/rn50_reg_safe.h
 
 $(obj)/r200.o: $(obj)/r200_reg_safe.h
@@ -42,6 +45,8 @@ $(obj)/r420.o: $(obj)/r420_reg_safe.h
 
 $(obj)/rs600.o: $(obj)/rs600_reg_safe.h
 
+$(obj)/r600_cs.o: $(obj)/r600_reg_safe.h
+
 radeon-y := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o \
        radeon_irq.o r300_cmdbuf.o r600_cp.o
 # add KMS driver
@@ -54,8 +59,10 @@ radeon-y += radeon_device.o radeon_kms.o \
        radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
        rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
        r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
-       r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o
+       r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
+       evergreen.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
+radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
 
 obj-$(CONFIG_DRM_RADEON)+= radeon.o
index 7f152f66f196394dc7ee1318e64d651d216dfe19..d75788feac6c6a64fe042e0e7d3e4a446d8b2e26 100644 (file)
@@ -881,8 +881,6 @@ static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
        uint8_t attr = U8((*ptr)++), shift;
        uint32_t saved, dst;
        int dptr = *ptr;
-       attr &= 0x38;
-       attr |= atom_def_dst[attr >> 3] << 6;
        SDEBUG("   dst: ");
        dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
        shift = atom_get_src(ctx, attr, ptr);
@@ -897,8 +895,6 @@ static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
        uint8_t attr = U8((*ptr)++), shift;
        uint32_t saved, dst;
        int dptr = *ptr;
-       attr &= 0x38;
-       attr |= atom_def_dst[attr >> 3] << 6;
        SDEBUG("   dst: ");
        dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
        shift = atom_get_src(ctx, attr, ptr);
index 91ad0d1c1b17e3d6101499bf4985894a250a392e..6732b5dd8ff4c48f1485fbc29a3499f5b07e85e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006-2007 Advanced Micro Devices, Inc.
+ * Copyright 2006-2007 Advanced Micro Devices, Inc.  
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
-/****************************************************************************/
+
+/****************************************************************************/ 
 /*Portion I: Definitions  shared between VBIOS and Driver                   */
 /****************************************************************************/
 
+
 #ifndef _ATOMBIOS_H
 #define _ATOMBIOS_H
 
 #endif
 
 #ifdef _H2INC
-#ifndef ULONG
-typedef unsigned long ULONG;
-#endif
+  #ifndef ULONG 
+    typedef unsigned long ULONG;
+  #endif
 
-#ifndef UCHAR
-typedef unsigned char UCHAR;
-#endif
+  #ifndef UCHAR
+    typedef unsigned char UCHAR;
+  #endif
 
-#ifndef USHORT
-typedef unsigned short USHORT;
-#endif
+  #ifndef USHORT 
+    typedef unsigned short USHORT;
+  #endif
 #endif
-
-#define ATOM_DAC_A            0
+      
+#define ATOM_DAC_A            0 
 #define ATOM_DAC_B            1
 #define ATOM_EXT_DAC          2
 
 #define ATOM_CRTC1            0
 #define ATOM_CRTC2            1
+#define ATOM_CRTC3            2
+#define ATOM_CRTC4            3
+#define ATOM_CRTC5            4
+#define ATOM_CRTC6            5
+#define ATOM_CRTC_INVALID     0xFF
 
 #define ATOM_DIGA             0
 #define ATOM_DIGB             1
 
 #define ATOM_PPLL1            0
 #define ATOM_PPLL2            1
+#define ATOM_DCPLL            2
+#define ATOM_PPLL_INVALID     0xFF
 
 #define ATOM_SCALER1          0
 #define ATOM_SCALER2          1
 
-#define ATOM_SCALER_DISABLE   0
-#define ATOM_SCALER_CENTER    1
-#define ATOM_SCALER_EXPANSION 2
-#define ATOM_SCALER_MULTI_EX  3
+#define ATOM_SCALER_DISABLE   0   
+#define ATOM_SCALER_CENTER    1   
+#define ATOM_SCALER_EXPANSION 2   
+#define ATOM_SCALER_MULTI_EX  3   
 
 #define ATOM_DISABLE          0
 #define ATOM_ENABLE           1
@@ -82,6 +91,7 @@ typedef unsigned short USHORT;
 #define ATOM_LCD_SELFTEST_START                                                                        (ATOM_DISABLE+5)
 #define ATOM_LCD_SELFTEST_STOP                                                                 (ATOM_ENABLE+5)
 #define ATOM_ENCODER_INIT                                        (ATOM_DISABLE+7)
+#define ATOM_GET_STATUS                         (ATOM_DISABLE+8)
 
 #define ATOM_BLANKING         1
 #define ATOM_BLANKING_OFF     0
@@ -114,7 +124,7 @@ typedef unsigned short USHORT;
 #define ATOM_DAC2_CV          ATOM_DAC1_CV
 #define ATOM_DAC2_NTSC        ATOM_DAC1_NTSC
 #define ATOM_DAC2_PAL         ATOM_DAC1_PAL
-
 #define ATOM_PM_ON            0
 #define ATOM_PM_STANDBY       1
 #define ATOM_PM_SUSPEND       2
@@ -134,6 +144,7 @@ typedef unsigned short USHORT;
 #define ATOM_PANEL_MISC_TEMPORAL           0x00000040
 #define ATOM_PANEL_MISC_API_ENABLED        0x00000080
 
+
 #define MEMTYPE_DDR1              "DDR1"
 #define MEMTYPE_DDR2              "DDR2"
 #define MEMTYPE_DDR3              "DDR3"
@@ -145,19 +156,19 @@ typedef unsigned short USHORT;
 
 /* Maximum size of that FireGL flag string */
 
-#define ATOM_FIREGL_FLAG_STRING     "FGL"      /* Flag used to enable FireGL Support */
-#define ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING  3 /* sizeof( ATOM_FIREGL_FLAG_STRING ) */
+#define ATOM_FIREGL_FLAG_STRING     "FGL"             //Flag used to enable FireGL Support
+#define ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING  3        //sizeof( ATOM_FIREGL_FLAG_STRING )
 
-#define ATOM_FAKE_DESKTOP_STRING    "DSK"      /* Flag used to enable mobile ASIC on Desktop */
-#define ATOM_MAX_SIZE_OF_FAKE_DESKTOP_STRING  ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING
+#define ATOM_FAKE_DESKTOP_STRING    "DSK"             //Flag used to enable mobile ASIC on Desktop
+#define ATOM_MAX_SIZE_OF_FAKE_DESKTOP_STRING  ATOM_MAX_SIZE_OF_FIREGL_FLAG_STRING 
 
-#define ATOM_M54T_FLAG_STRING       "M54T"     /* Flag used to enable M54T Support */
-#define ATOM_MAX_SIZE_OF_M54T_FLAG_STRING    4 /* sizeof( ATOM_M54T_FLAG_STRING ) */
+#define ATOM_M54T_FLAG_STRING       "M54T"            //Flag used to enable M54T Support
+#define ATOM_MAX_SIZE_OF_M54T_FLAG_STRING    4        //sizeof( ATOM_M54T_FLAG_STRING )
 
 #define HW_ASSISTED_I2C_STATUS_FAILURE          2
 #define HW_ASSISTED_I2C_STATUS_SUCCESS          1
 
-#pragma pack(1)                        /* BIOS data must use byte aligment */
+#pragma pack(1)                                       /* BIOS data must use byte aligment */
 
 /*  Define offset to location of ROM header. */
 
@@ -165,367 +176,410 @@ typedef unsigned short USHORT;
 #define OFFSET_TO_ATOM_ROM_IMAGE_SIZE                              0x00000002L
 
 #define OFFSET_TO_ATOMBIOS_ASIC_BUS_MEM_TYPE    0x94
-#define MAXSIZE_OF_ATOMBIOS_ASIC_BUS_MEM_TYPE   20     /* including the terminator 0x0! */
+#define MAXSIZE_OF_ATOMBIOS_ASIC_BUS_MEM_TYPE   20    /* including the terminator 0x0! */
 #define        OFFSET_TO_GET_ATOMBIOS_STRINGS_NUMBER           0x002f
 #define        OFFSET_TO_GET_ATOMBIOS_STRINGS_START            0x006e
 
 /* Common header for all ROM Data tables.
-  Every table pointed  _ATOM_MASTER_DATA_TABLE has this common header.
+  Every table pointed  _ATOM_MASTER_DATA_TABLE has this common header. 
   And the pointer actually points to this header. */
 
-typedef struct _ATOM_COMMON_TABLE_HEADER {
-       USHORT usStructureSize;
-       UCHAR ucTableFormatRevision;    /*Change it when the Parser is not backward compatible */
-       UCHAR ucTableContentRevision;   /*Change it only when the table needs to change but the firmware */
-       /*Image can't be updated, while Driver needs to carry the new table! */
-} ATOM_COMMON_TABLE_HEADER;
-
-typedef struct _ATOM_ROM_HEADER {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR uaFirmWareSignature[4];   /*Signature to distinguish between Atombios and non-atombios,
-                                          atombios should init it as "ATOM", don't change the position */
-       USHORT usBiosRuntimeSegmentAddress;
-       USHORT usProtectedModeInfoOffset;
-       USHORT usConfigFilenameOffset;
-       USHORT usCRC_BlockOffset;
-       USHORT usBIOS_BootupMessageOffset;
-       USHORT usInt10Offset;
-       USHORT usPciBusDevInitCode;
-       USHORT usIoBaseAddress;
-       USHORT usSubsystemVendorID;
-       USHORT usSubsystemID;
-       USHORT usPCI_InfoOffset;
-       USHORT usMasterCommandTableOffset;      /*Offset for SW to get all command table offsets, Don't change the position */
-       USHORT usMasterDataTableOffset; /*Offset for SW to get all data table offsets, Don't change the position */
-       UCHAR ucExtendedFunctionCode;
-       UCHAR ucReserved;
-} ATOM_ROM_HEADER;
+typedef struct _ATOM_COMMON_TABLE_HEADER
+{
+  USHORT usStructureSize;
+  UCHAR  ucTableFormatRevision;   /*Change it when the Parser is not backward compatible */
+  UCHAR  ucTableContentRevision;  /*Change it only when the table needs to change but the firmware */
+                                  /*Image can't be updated, while Driver needs to carry the new table! */
+}ATOM_COMMON_TABLE_HEADER;
+
+typedef struct _ATOM_ROM_HEADER
+{
+  ATOM_COMMON_TABLE_HEADER             sHeader;
+  UCHAR         uaFirmWareSignature[4];    /*Signature to distinguish between Atombios and non-atombios, 
+                                      atombios should init it as "ATOM", don't change the position */
+  USHORT usBiosRuntimeSegmentAddress;
+  USHORT usProtectedModeInfoOffset;
+  USHORT usConfigFilenameOffset;
+  USHORT usCRC_BlockOffset;
+  USHORT usBIOS_BootupMessageOffset;
+  USHORT usInt10Offset;
+  USHORT usPciBusDevInitCode;
+  USHORT usIoBaseAddress;
+  USHORT usSubsystemVendorID;
+  USHORT usSubsystemID;
+  USHORT usPCI_InfoOffset; 
+  USHORT usMasterCommandTableOffset; /*Offset for SW to get all command table offsets, Don't change the position */
+  USHORT usMasterDataTableOffset;   /*Offset for SW to get all data table offsets, Don't change the position */
+  UCHAR  ucExtendedFunctionCode;
+  UCHAR  ucReserved;
+}ATOM_ROM_HEADER;
 
 /*==============================Command Table Portion==================================== */
 
 #ifdef UEFI_BUILD
-#define        UTEMP   USHORT
-#define        USHORT  void*
+       #define UTEMP   USHORT
+       #define USHORT  void*
 #endif
 
-typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES {
-       USHORT ASIC_Init;       /* Function Table, used by various SW components,latest version 1.1 */
-       USHORT GetDisplaySurfaceSize;   /* Atomic Table,  Used by Bios when enabling HW ICON */
-       USHORT ASIC_RegistersInit;      /* Atomic Table,  indirectly used by various SW components,called from ASIC_Init */
-       USHORT VRAM_BlockVenderDetection;       /* Atomic Table,  used only by Bios */
-       USHORT DIGxEncoderControl;      /* Only used by Bios */
-       USHORT MemoryControllerInit;    /* Atomic Table,  indirectly used by various SW components,called from ASIC_Init */
-       USHORT EnableCRTCMemReq;        /* Function Table,directly used by various SW components,latest version 2.1 */
-       USHORT MemoryParamAdjust;       /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock if needed */
-       USHORT DVOEncoderControl;       /* Function Table,directly used by various SW components,latest version 1.2 */
-       USHORT GPIOPinControl;  /* Atomic Table,  only used by Bios */
-       USHORT SetEngineClock;  /*Function Table,directly used by various SW components,latest version 1.1 */
-       USHORT SetMemoryClock;  /* Function Table,directly used by various SW components,latest version 1.1 */
-       USHORT SetPixelClock;   /*Function Table,directly used by various SW components,latest version 1.2 */
-       USHORT DynamicClockGating;      /* Atomic Table,  indirectly used by various SW components,called from ASIC_Init */
-       USHORT ResetMemoryDLL;  /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock */
-       USHORT ResetMemoryDevice;       /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock */
-       USHORT MemoryPLLInit;
-       USHORT AdjustDisplayPll;        /* only used by Bios */
-       USHORT AdjustMemoryController;  /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock */
-       USHORT EnableASIC_StaticPwrMgt; /* Atomic Table,  only used by Bios */
-       USHORT ASIC_StaticPwrMgtStatusChange;   /* Obsolete, only used by Bios */
-       USHORT DAC_LoadDetection;       /* Atomic Table,  directly used by various SW components,latest version 1.2 */
-       USHORT LVTMAEncoderControl;     /* Atomic Table,directly used by various SW components,latest version 1.3 */
-       USHORT LCD1OutputControl;       /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT DAC1EncoderControl;      /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT DAC2EncoderControl;      /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT DVOOutputControl;        /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT CV1OutputControl;        /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT GetConditionalGoldenSetting;     /* only used by Bios */
-       USHORT TVEncoderControl;        /* Function Table,directly used by various SW components,latest version 1.1 */
-       USHORT TMDSAEncoderControl;     /* Atomic Table,  directly used by various SW components,latest version 1.3 */
-       USHORT LVDSEncoderControl;      /* Atomic Table,  directly used by various SW components,latest version 1.3 */
-       USHORT TV1OutputControl;        /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT EnableScaler;    /* Atomic Table,  used only by Bios */
-       USHORT BlankCRTC;       /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT EnableCRTC;      /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT GetPixelClock;   /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT EnableVGA_Render;        /* Function Table,directly used by various SW components,latest version 1.1 */
-       USHORT EnableVGA_Access;        /* Obsolete ,     only used by Bios */
-       USHORT SetCRTC_Timing;  /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT SetCRTC_OverScan;        /* Atomic Table,  used by various SW components,latest version 1.1 */
-       USHORT SetCRTC_Replication;     /* Atomic Table,  used only by Bios */
-       USHORT SelectCRTC_Source;       /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT EnableGraphSurfaces;     /* Atomic Table,  used only by Bios */
-       USHORT UpdateCRTC_DoubleBufferRegisters;
-       USHORT LUT_AutoFill;    /* Atomic Table,  only used by Bios */
-       USHORT EnableHW_IconCursor;     /* Atomic Table,  only used by Bios */
-       USHORT GetMemoryClock;  /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT GetEngineClock;  /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT SetCRTC_UsingDTDTiming;  /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT ExternalEncoderControl;  /* Atomic Table,  directly used by various SW components,latest version 2.1 */
-       USHORT LVTMAOutputControl;      /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT VRAM_BlockDetectionByStrap;      /* Atomic Table,  used only by Bios */
-       USHORT MemoryCleanUp;   /* Atomic Table,  only used by Bios */
-       USHORT ProcessI2cChannelTransaction;    /* Function Table,only used by Bios */
-       USHORT WriteOneByteToHWAssistedI2C;     /* Function Table,indirectly used by various SW components */
-       USHORT ReadHWAssistedI2CStatus; /* Atomic Table,  indirectly used by various SW components */
-       USHORT SpeedFanControl; /* Function Table,indirectly used by various SW components,called from ASIC_Init */
-       USHORT PowerConnectorDetection; /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT MC_Synchronization;      /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock */
-       USHORT ComputeMemoryEnginePLL;  /* Atomic Table,  indirectly used by various SW components,called from SetMemory/EngineClock */
-       USHORT MemoryRefreshConversion; /* Atomic Table,  indirectly used by various SW components,called from SetMemory or SetEngineClock */
-       USHORT VRAM_GetCurrentInfoBlock;        /* Atomic Table,  used only by Bios */
-       USHORT DynamicMemorySettings;   /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock */
-       USHORT MemoryTraining;  /* Atomic Table,  used only by Bios */
-       USHORT EnableSpreadSpectrumOnPPLL;      /* Atomic Table,  directly used by various SW components,latest version 1.2 */
-       USHORT TMDSAOutputControl;      /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT SetVoltage;      /* Function Table,directly and/or indirectly used by various SW components,latest version 1.1 */
-       USHORT DAC1OutputControl;       /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT DAC2OutputControl;       /* Atomic Table,  directly used by various SW components,latest version 1.1 */
-       USHORT SetupHWAssistedI2CStatus;        /* Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" */
-       USHORT ClockSource;     /* Atomic Table,  indirectly used by various SW components,called from ASIC_Init */
-       USHORT MemoryDeviceInit;        /* Atomic Table,  indirectly used by various SW components,called from SetMemoryClock */
-       USHORT EnableYUV;       /* Atomic Table,  indirectly used by various SW components,called from EnableVGARender */
-       USHORT DIG1EncoderControl;      /* Atomic Table,directly used by various SW components,latest version 1.1 */
-       USHORT DIG2EncoderControl;      /* Atomic Table,directly used by various SW components,latest version 1.1 */
-       USHORT DIG1TransmitterControl;  /* Atomic Table,directly used by various SW components,latest version 1.1 */
-       USHORT DIG2TransmitterControl;  /* Atomic Table,directly used by various SW components,latest version 1.1 */
-       USHORT ProcessAuxChannelTransaction;    /* Function Table,only used by Bios */
-       USHORT DPEncoderService;        /* Function Table,only used by Bios */
-} ATOM_MASTER_LIST_OF_COMMAND_TABLES;
-
-/*  For backward compatible */
+typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
+  USHORT ASIC_Init;                              //Function Table, used by various SW components,latest version 1.1
+  USHORT GetDisplaySurfaceSize;                  //Atomic Table,  Used by Bios when enabling HW ICON
+  USHORT ASIC_RegistersInit;                     //Atomic Table,  indirectly used by various SW components,called from ASIC_Init
+  USHORT VRAM_BlockVenderDetection;              //Atomic Table,  used only by Bios
+  USHORT DIGxEncoderControl;                                                                            //Only used by Bios
+  USHORT MemoryControllerInit;                   //Atomic Table,  indirectly used by various SW components,called from ASIC_Init
+  USHORT EnableCRTCMemReq;                       //Function Table,directly used by various SW components,latest version 2.1
+  USHORT MemoryParamAdjust;                                                                             //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock if needed
+  USHORT DVOEncoderControl;                      //Function Table,directly used by various SW components,latest version 1.2
+  USHORT GPIOPinControl;                                                                                                //Atomic Table,  only used by Bios
+  USHORT SetEngineClock;                         //Function Table,directly used by various SW components,latest version 1.1
+  USHORT SetMemoryClock;                         //Function Table,directly used by various SW components,latest version 1.1
+  USHORT SetPixelClock;                          //Function Table,directly used by various SW components,latest version 1.2  
+  USHORT DynamicClockGating;                     //Atomic Table,  indirectly used by various SW components,called from ASIC_Init
+  USHORT ResetMemoryDLL;                         //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock
+  USHORT ResetMemoryDevice;                      //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock
+  USHORT MemoryPLLInit;
+  USHORT AdjustDisplayPll;                                                                                             //only used by Bios
+  USHORT AdjustMemoryController;                 //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock                
+  USHORT EnableASIC_StaticPwrMgt;                //Atomic Table,  only used by Bios
+  USHORT ASIC_StaticPwrMgtStatusChange;          //Obsolete ,     only used by Bios   
+  USHORT DAC_LoadDetection;                      //Atomic Table,  directly used by various SW components,latest version 1.2  
+  USHORT LVTMAEncoderControl;                    //Atomic Table,directly used by various SW components,latest version 1.3
+  USHORT LCD1OutputControl;                      //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT DAC1EncoderControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1  
+  USHORT DAC2EncoderControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT DVOOutputControl;                       //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT CV1OutputControl;                       //Atomic Table,  Atomic Table,  Obsolete from Ry6xx, use DAC2 Output instead 
+  USHORT GetConditionalGoldenSetting;            //only used by Bios
+  USHORT TVEncoderControl;                       //Function Table,directly used by various SW components,latest version 1.1
+  USHORT TMDSAEncoderControl;                    //Atomic Table,  directly used by various SW components,latest version 1.3
+  USHORT LVDSEncoderControl;                     //Atomic Table,  directly used by various SW components,latest version 1.3
+  USHORT TV1OutputControl;                       //Atomic Table,  Obsolete from Ry6xx, use DAC2 Output instead
+  USHORT EnableScaler;                           //Atomic Table,  used only by Bios
+  USHORT BlankCRTC;                              //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT EnableCRTC;                             //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT GetPixelClock;                          //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT EnableVGA_Render;                       //Function Table,directly used by various SW components,latest version 1.1
+  USHORT GetSCLKOverMCLKRatio;                   //Atomic Table,  only used by Bios
+  USHORT SetCRTC_Timing;                         //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT SetCRTC_OverScan;                       //Atomic Table,  used by various SW components,latest version 1.1 
+  USHORT SetCRTC_Replication;                    //Atomic Table,  used only by Bios
+  USHORT SelectCRTC_Source;                      //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT EnableGraphSurfaces;                    //Atomic Table,  used only by Bios
+  USHORT UpdateCRTC_DoubleBufferRegisters;
+  USHORT LUT_AutoFill;                           //Atomic Table,  only used by Bios
+  USHORT EnableHW_IconCursor;                    //Atomic Table,  only used by Bios
+  USHORT GetMemoryClock;                         //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT GetEngineClock;                         //Atomic Table,  directly used by various SW components,latest version 1.1 
+  USHORT SetCRTC_UsingDTDTiming;                 //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT ExternalEncoderControl;                 //Atomic Table,  directly used by various SW components,latest version 2.1
+  USHORT LVTMAOutputControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT VRAM_BlockDetectionByStrap;             //Atomic Table,  used only by Bios
+  USHORT MemoryCleanUp;                          //Atomic Table,  only used by Bios    
+  USHORT ProcessI2cChannelTransaction;           //Function Table,only used by Bios
+  USHORT WriteOneByteToHWAssistedI2C;            //Function Table,indirectly used by various SW components 
+  USHORT ReadHWAssistedI2CStatus;                //Atomic Table,  indirectly used by various SW components
+  USHORT SpeedFanControl;                        //Function Table,indirectly used by various SW components,called from ASIC_Init
+  USHORT PowerConnectorDetection;                //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT MC_Synchronization;                     //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock
+  USHORT ComputeMemoryEnginePLL;                 //Atomic Table,  indirectly used by various SW components,called from SetMemory/EngineClock
+  USHORT MemoryRefreshConversion;                //Atomic Table,  indirectly used by various SW components,called from SetMemory or SetEngineClock
+  USHORT VRAM_GetCurrentInfoBlock;               //Atomic Table,  used only by Bios
+  USHORT DynamicMemorySettings;                  //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock
+  USHORT MemoryTraining;                         //Atomic Table,  used only by Bios
+  USHORT EnableSpreadSpectrumOnPPLL;             //Atomic Table,  directly used by various SW components,latest version 1.2
+  USHORT TMDSAOutputControl;                     //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT SetVoltage;                             //Function Table,directly and/or indirectly used by various SW components,latest version 1.1
+  USHORT DAC1OutputControl;                      //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT DAC2OutputControl;                      //Atomic Table,  directly used by various SW components,latest version 1.1
+  USHORT SetupHWAssistedI2CStatus;               //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C"
+  USHORT ClockSource;                            //Atomic Table,  indirectly used by various SW components,called from ASIC_Init
+  USHORT MemoryDeviceInit;                       //Atomic Table,  indirectly used by various SW components,called from SetMemoryClock
+  USHORT EnableYUV;                              //Atomic Table,  indirectly used by various SW components,called from EnableVGARender
+  USHORT DIG1EncoderControl;                     //Atomic Table,directly used by various SW components,latest version 1.1
+  USHORT DIG2EncoderControl;                     //Atomic Table,directly used by various SW components,latest version 1.1
+  USHORT DIG1TransmitterControl;                 //Atomic Table,directly used by various SW components,latest version 1.1
+  USHORT DIG2TransmitterControl;                      //Atomic Table,directly used by various SW components,latest version 1.1 
+  USHORT ProcessAuxChannelTransaction;                                  //Function Table,only used by Bios
+  USHORT DPEncoderService;                                                                                      //Function Table,only used by Bios
+}ATOM_MASTER_LIST_OF_COMMAND_TABLES;   
+
+// For backward compatible 
 #define ReadEDIDFromHWAssistedI2C                ProcessI2cChannelTransaction
 #define UNIPHYTransmitterControl                                                    DIG1TransmitterControl
 #define LVTMATransmitterControl                                                             DIG2TransmitterControl
 #define SetCRTC_DPM_State                        GetConditionalGoldenSetting
 #define SetUniphyInstance                        ASIC_StaticPwrMgtStatusChange
+#define HPDInterruptService                      ReadHWAssistedI2CStatus
+#define EnableVGA_Access                         GetSCLKOverMCLKRatio
 
-typedef struct _ATOM_MASTER_COMMAND_TABLE {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_MASTER_LIST_OF_COMMAND_TABLES ListOfCommandTables;
-} ATOM_MASTER_COMMAND_TABLE;
-
-/****************************************************************************/
-/*  Structures used in every command table */
-/****************************************************************************/
-typedef struct _ATOM_TABLE_ATTRIBUTE {
+typedef struct _ATOM_MASTER_COMMAND_TABLE
+{
+  ATOM_COMMON_TABLE_HEADER           sHeader;
+  ATOM_MASTER_LIST_OF_COMMAND_TABLES ListOfCommandTables;
+}ATOM_MASTER_COMMAND_TABLE;
+
+/****************************************************************************/ 
+// Structures used in every command table
+/****************************************************************************/ 
+typedef struct _ATOM_TABLE_ATTRIBUTE
+{
 #if ATOM_BIG_ENDIAN
-       USHORT UpdatedByUtility:1;      /* [15]=Table updated by utility flag */
-       USHORT PS_SizeInBytes:7;        /* [14:8]=Size of parameter space in Bytes (multiple of a dword), */
-       USHORT WS_SizeInBytes:8;        /* [7:0]=Size of workspace in Bytes (in multiple of a dword), */
+  USHORT  UpdatedByUtility:1;         //[15]=Table updated by utility flag
+  USHORT  PS_SizeInBytes:7;           //[14:8]=Size of parameter space in Bytes (multiple of a dword), 
+  USHORT  WS_SizeInBytes:8;           //[7:0]=Size of workspace in Bytes (in multiple of a dword), 
 #else
-       USHORT WS_SizeInBytes:8;        /* [7:0]=Size of workspace in Bytes (in multiple of a dword), */
-       USHORT PS_SizeInBytes:7;        /* [14:8]=Size of parameter space in Bytes (multiple of a dword), */
-       USHORT UpdatedByUtility:1;      /* [15]=Table updated by utility flag */
+  USHORT  WS_SizeInBytes:8;           //[7:0]=Size of workspace in Bytes (in multiple of a dword), 
+  USHORT  PS_SizeInBytes:7;           //[14:8]=Size of parameter space in Bytes (multiple of a dword), 
+  USHORT  UpdatedByUtility:1;         //[15]=Table updated by utility flag
 #endif
-} ATOM_TABLE_ATTRIBUTE;
-
-typedef union _ATOM_TABLE_ATTRIBUTE_ACCESS {
-       ATOM_TABLE_ATTRIBUTE sbfAccess;
-       USHORT susAccess;
-} ATOM_TABLE_ATTRIBUTE_ACCESS;
+}ATOM_TABLE_ATTRIBUTE;
 
-/****************************************************************************/
-/*  Common header for all command tables. */
-/*  Every table pointed by _ATOM_MASTER_COMMAND_TABLE has this common header. */
-/*  And the pointer actually points to this header. */
-/****************************************************************************/
-typedef struct _ATOM_COMMON_ROM_COMMAND_TABLE_HEADER {
-       ATOM_COMMON_TABLE_HEADER CommonHeader;
-       ATOM_TABLE_ATTRIBUTE TableAttribute;
-} ATOM_COMMON_ROM_COMMAND_TABLE_HEADER;
+typedef union _ATOM_TABLE_ATTRIBUTE_ACCESS
+{
+  ATOM_TABLE_ATTRIBUTE sbfAccess;
+  USHORT               susAccess;
+}ATOM_TABLE_ATTRIBUTE_ACCESS;
+
+/****************************************************************************/ 
+// Common header for all command tables.
+// Every table pointed by _ATOM_MASTER_COMMAND_TABLE has this common header. 
+// And the pointer actually points to this header.
+/****************************************************************************/ 
+typedef struct _ATOM_COMMON_ROM_COMMAND_TABLE_HEADER
+{
+  ATOM_COMMON_TABLE_HEADER CommonHeader;
+  ATOM_TABLE_ATTRIBUTE     TableAttribute;     
+}ATOM_COMMON_ROM_COMMAND_TABLE_HEADER;
 
-/****************************************************************************/
-/*  Structures used by ComputeMemoryEnginePLLTable */
-/****************************************************************************/
+/****************************************************************************/ 
+// Structures used by ComputeMemoryEnginePLLTable
+/****************************************************************************/ 
 #define COMPUTE_MEMORY_PLL_PARAM        1
 #define COMPUTE_ENGINE_PLL_PARAM        2
 
-typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS {
-       ULONG ulClock;          /* When returen, it's the re-calculated clock based on given Fb_div Post_Div and ref_div */
-       UCHAR ucAction;         /* 0:reserved //1:Memory //2:Engine */
-       UCHAR ucReserved;       /* may expand to return larger Fbdiv later */
-       UCHAR ucFbDiv;          /* return value */
-       UCHAR ucPostDiv;        /* return value */
-} COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS;
-
-typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2 {
-       ULONG ulClock;          /* When return, [23:0] return real clock */
-       UCHAR ucAction;         /* 0:reserved;COMPUTE_MEMORY_PLL_PARAM:Memory;COMPUTE_ENGINE_PLL_PARAM:Engine. it return ref_div to be written to register */
-       USHORT usFbDiv;         /* return Feedback value to be written to register */
-       UCHAR ucPostDiv;        /* return post div to be written to register */
-} COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2;
+typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS
+{
+  ULONG   ulClock;        //When returen, it's the re-calculated clock based on given Fb_div Post_Div and ref_div
+  UCHAR   ucAction;       //0:reserved //1:Memory //2:Engine  
+  UCHAR   ucReserved;     //may expand to return larger Fbdiv later
+  UCHAR   ucFbDiv;        //return value
+  UCHAR   ucPostDiv;      //return value
+}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS;
+
+typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2
+{
+  ULONG   ulClock;        //When return, [23:0] return real clock 
+  UCHAR   ucAction;       //0:reserved;COMPUTE_MEMORY_PLL_PARAM:Memory;COMPUTE_ENGINE_PLL_PARAM:Engine. it return ref_div to be written to register
+  USHORT  usFbDiv;                 //return Feedback value to be written to register
+  UCHAR   ucPostDiv;      //return post div to be written to register
+}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V2;
 #define COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION   COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS
 
-#define SET_CLOCK_FREQ_MASK                     0x00FFFFFF     /* Clock change tables only take bit [23:0] as the requested clock value */
-#define USE_NON_BUS_CLOCK_MASK                  0x01000000     /* Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) */
-#define USE_MEMORY_SELF_REFRESH_MASK            0x02000000     /* Only applicable to memory clock change, when set, using memory self refresh during clock transition */
-#define SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE   0x04000000     /* Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change */
-#define FIRST_TIME_CHANGE_CLOCK                                                                        0x08000000      /* Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup */
-#define SKIP_SW_PROGRAM_PLL                                                                                    0x10000000      /* Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL */
+
+#define SET_CLOCK_FREQ_MASK                     0x00FFFFFF  //Clock change tables only take bit [23:0] as the requested clock value
+#define USE_NON_BUS_CLOCK_MASK                  0x01000000  //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa)
+#define USE_MEMORY_SELF_REFRESH_MASK            0x02000000     //Only applicable to memory clock change, when set, using memory self refresh during clock transition
+#define SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE   0x04000000  //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change
+#define FIRST_TIME_CHANGE_CLOCK                                                                        0x08000000      //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup
+#define SKIP_SW_PROGRAM_PLL                                                                                    0x10000000      //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL
 #define USE_SS_ENABLED_PIXEL_CLOCK  USE_NON_BUS_CLOCK_MASK
 
-#define b3USE_NON_BUS_CLOCK_MASK                  0x01 /* Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa) */
-#define b3USE_MEMORY_SELF_REFRESH                 0x02 /* Only applicable to memory clock change, when set, using memory self refresh during clock transition */
-#define b3SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE   0x04 /* Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change */
-#define b3FIRST_TIME_CHANGE_CLOCK                                                                      0x08    /* Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup */
-#define b3SKIP_SW_PROGRAM_PLL                                                                                  0x10    /* Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL */
+#define b3USE_NON_BUS_CLOCK_MASK                  0x01       //Applicable to both memory and engine clock change, when set, it uses another clock as the temporary clock (engine uses memory and vice versa)
+#define b3USE_MEMORY_SELF_REFRESH                 0x02      //Only applicable to memory clock change, when set, using memory self refresh during clock transition
+#define b3SKIP_INTERNAL_MEMORY_PARAMETER_CHANGE   0x04       //Only applicable to memory clock change, when set, the table will skip predefined internal memory parameter change
+#define b3FIRST_TIME_CHANGE_CLOCK                                                                      0x08       //Applicable to both memory and engine clock change,when set, it means this is 1st time to change clock after ASIC bootup
+#define b3SKIP_SW_PROGRAM_PLL                                                                                  0x10                     //Applicable to both memory and engine clock change, when set, it means the table will not program SPLL/MPLL
 
-typedef struct _ATOM_COMPUTE_CLOCK_FREQ {
+typedef struct _ATOM_COMPUTE_CLOCK_FREQ
+{
 #if ATOM_BIG_ENDIAN
-       ULONG ulComputeClockFlag:8;     /*  =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM */
-       ULONG ulClockFreq:24;   /*  in unit of 10kHz */
+  ULONG ulComputeClockFlag:8;                 // =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM
+  ULONG ulClockFreq:24;                       // in unit of 10kHz
 #else
-       ULONG ulClockFreq:24;   /*  in unit of 10kHz */
-       ULONG ulComputeClockFlag:8;     /*  =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM */
+  ULONG ulClockFreq:24;                       // in unit of 10kHz
+  ULONG ulComputeClockFlag:8;                 // =1: COMPUTE_MEMORY_PLL_PARAM, =2: COMPUTE_ENGINE_PLL_PARAM
 #endif
-} ATOM_COMPUTE_CLOCK_FREQ;
-
-typedef struct _ATOM_S_MPLL_FB_DIVIDER {
-       USHORT usFbDivFrac;
-       USHORT usFbDiv;
-} ATOM_S_MPLL_FB_DIVIDER;
+}ATOM_COMPUTE_CLOCK_FREQ;
 
-typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 {
-       union {
-               ATOM_COMPUTE_CLOCK_FREQ ulClock;        /* Input Parameter */
-               ATOM_S_MPLL_FB_DIVIDER ulFbDiv; /* Output Parameter */
-       };
-       UCHAR ucRefDiv;         /* Output Parameter */
-       UCHAR ucPostDiv;        /* Output Parameter */
-       UCHAR ucCntlFlag;       /* Output Parameter */
-       UCHAR ucReserved;
-} COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3;
+typedef struct _ATOM_S_MPLL_FB_DIVIDER
+{
+  USHORT usFbDivFrac;  
+  USHORT usFbDiv;  
+}ATOM_S_MPLL_FB_DIVIDER;
 
-/*  ucCntlFlag */
+typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3
+{
+  union
+  {
+    ATOM_COMPUTE_CLOCK_FREQ  ulClock;         //Input Parameter
+    ATOM_S_MPLL_FB_DIVIDER   ulFbDiv;         //Output Parameter
+  };
+  UCHAR   ucRefDiv;                           //Output Parameter      
+  UCHAR   ucPostDiv;                          //Output Parameter      
+  UCHAR   ucCntlFlag;                         //Output Parameter      
+  UCHAR   ucReserved;
+}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3;
+
+// ucCntlFlag
 #define ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN          1
 #define ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE            2
 #define ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE         4
+#define ATOM_PLL_CNTL_FLAG_SPLL_ISPARE_9                                               8
 
-typedef struct _DYNAMICE_MEMORY_SETTINGS_PARAMETER {
-       ATOM_COMPUTE_CLOCK_FREQ ulClock;
-       ULONG ulReserved[2];
-} DYNAMICE_MEMORY_SETTINGS_PARAMETER;
 
-typedef struct _DYNAMICE_ENGINE_SETTINGS_PARAMETER {
-       ATOM_COMPUTE_CLOCK_FREQ ulClock;
-       ULONG ulMemoryClock;
-       ULONG ulReserved;
-} DYNAMICE_ENGINE_SETTINGS_PARAMETER;
-
-/****************************************************************************/
-/*  Structures used by SetEngineClockTable */
-/****************************************************************************/
-typedef struct _SET_ENGINE_CLOCK_PARAMETERS {
-       ULONG ulTargetEngineClock;      /* In 10Khz unit */
-} SET_ENGINE_CLOCK_PARAMETERS;
-
-typedef struct _SET_ENGINE_CLOCK_PS_ALLOCATION {
-       ULONG ulTargetEngineClock;      /* In 10Khz unit */
-       COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved;
-} SET_ENGINE_CLOCK_PS_ALLOCATION;
+// V4 are only used for APU which PLL outside GPU
+typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4
+{
+#if ATOM_BIG_ENDIAN
+  ULONG  ucPostDiv;          //return parameter: post divider which is used to program to register directly
+  ULONG  ulClock:24;         //Input= target clock, output = actual clock 
+#else
+  ULONG  ulClock:24;         //Input= target clock, output = actual clock 
+  ULONG  ucPostDiv;          //return parameter: post divider which is used to program to register directly
+#endif
+}COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4;
 
-/****************************************************************************/
-/*  Structures used by SetMemoryClockTable */
-/****************************************************************************/
-typedef struct _SET_MEMORY_CLOCK_PARAMETERS {
-       ULONG ulTargetMemoryClock;      /* In 10Khz unit */
-} SET_MEMORY_CLOCK_PARAMETERS;
+typedef struct _DYNAMICE_MEMORY_SETTINGS_PARAMETER
+{
+  ATOM_COMPUTE_CLOCK_FREQ ulClock;
+  ULONG ulReserved[2];
+}DYNAMICE_MEMORY_SETTINGS_PARAMETER;
 
-typedef struct _SET_MEMORY_CLOCK_PS_ALLOCATION {
-       ULONG ulTargetMemoryClock;      /* In 10Khz unit */
-       COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved;
-} SET_MEMORY_CLOCK_PS_ALLOCATION;
+typedef struct _DYNAMICE_ENGINE_SETTINGS_PARAMETER
+{
+  ATOM_COMPUTE_CLOCK_FREQ ulClock;
+  ULONG ulMemoryClock;
+  ULONG ulReserved;
+}DYNAMICE_ENGINE_SETTINGS_PARAMETER;
+
+/****************************************************************************/ 
+// Structures used by SetEngineClockTable
+/****************************************************************************/ 
+typedef struct _SET_ENGINE_CLOCK_PARAMETERS
+{
+  ULONG ulTargetEngineClock;          //In 10Khz unit
+}SET_ENGINE_CLOCK_PARAMETERS;
 
-/****************************************************************************/
-/*  Structures used by ASIC_Init.ctb */
-/****************************************************************************/
-typedef struct _ASIC_INIT_PARAMETERS {
-       ULONG ulDefaultEngineClock;     /* In 10Khz unit */
-       ULONG ulDefaultMemoryClock;     /* In 10Khz unit */
-} ASIC_INIT_PARAMETERS;
+typedef struct _SET_ENGINE_CLOCK_PS_ALLOCATION
+{
+  ULONG ulTargetEngineClock;          //In 10Khz unit
+  COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved;
+}SET_ENGINE_CLOCK_PS_ALLOCATION;
+
+/****************************************************************************/ 
+// Structures used by SetMemoryClockTable
+/****************************************************************************/ 
+typedef struct _SET_MEMORY_CLOCK_PARAMETERS
+{
+  ULONG ulTargetMemoryClock;          //In 10Khz unit
+}SET_MEMORY_CLOCK_PARAMETERS;
 
-typedef struct _ASIC_INIT_PS_ALLOCATION {
-       ASIC_INIT_PARAMETERS sASICInitClocks;
-       SET_ENGINE_CLOCK_PS_ALLOCATION sReserved;       /* Caller doesn't need to init this structure */
-} ASIC_INIT_PS_ALLOCATION;
+typedef struct _SET_MEMORY_CLOCK_PS_ALLOCATION
+{
+  ULONG ulTargetMemoryClock;          //In 10Khz unit
+  COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_PS_ALLOCATION sReserved;
+}SET_MEMORY_CLOCK_PS_ALLOCATION;
+
+/****************************************************************************/ 
+// Structures used by ASIC_Init.ctb
+/****************************************************************************/ 
+typedef struct _ASIC_INIT_PARAMETERS
+{
+  ULONG ulDefaultEngineClock;         //In 10Khz unit
+  ULONG ulDefaultMemoryClock;         //In 10Khz unit
+}ASIC_INIT_PARAMETERS;
 
-/****************************************************************************/
-/*  Structure used by DynamicClockGatingTable.ctb */
-/****************************************************************************/
-typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS {
-       UCHAR ucEnable;         /*  ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucPadding[3];
-} DYNAMIC_CLOCK_GATING_PARAMETERS;
+typedef struct _ASIC_INIT_PS_ALLOCATION
+{
+  ASIC_INIT_PARAMETERS sASICInitClocks;
+  SET_ENGINE_CLOCK_PS_ALLOCATION sReserved; //Caller doesn't need to init this structure
+}ASIC_INIT_PS_ALLOCATION;
+
+/****************************************************************************/ 
+// Structure used by DynamicClockGatingTable.ctb
+/****************************************************************************/ 
+typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS 
+{
+  UCHAR ucEnable;                     // ATOM_ENABLE or ATOM_DISABLE
+  UCHAR ucPadding[3];
+}DYNAMIC_CLOCK_GATING_PARAMETERS;
 #define  DYNAMIC_CLOCK_GATING_PS_ALLOCATION  DYNAMIC_CLOCK_GATING_PARAMETERS
 
-/****************************************************************************/
-/*  Structure used by EnableASIC_StaticPwrMgtTable.ctb */
-/****************************************************************************/
-typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS {
-       UCHAR ucEnable;         /*  ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucPadding[3];
-} ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS;
+/****************************************************************************/ 
+// Structure used by EnableASIC_StaticPwrMgtTable.ctb
+/****************************************************************************/ 
+typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS
+{
+  UCHAR ucEnable;                     // ATOM_ENABLE or ATOM_DISABLE
+  UCHAR ucPadding[3];
+}ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS;
 #define ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION  ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by DAC_LoadDetectionTable.ctb */
-/****************************************************************************/
-typedef struct _DAC_LOAD_DETECTION_PARAMETERS {
-       USHORT usDeviceID;      /* {ATOM_DEVICE_CRTx_SUPPORT,ATOM_DEVICE_TVx_SUPPORT,ATOM_DEVICE_CVx_SUPPORT} */
-       UCHAR ucDacType;        /* {ATOM_DAC_A,ATOM_DAC_B, ATOM_EXT_DAC} */
-       UCHAR ucMisc;           /* Valid only when table revision =1.3 and above */
-} DAC_LOAD_DETECTION_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by DAC_LoadDetectionTable.ctb
+/****************************************************************************/ 
+typedef struct _DAC_LOAD_DETECTION_PARAMETERS
+{
+  USHORT usDeviceID;                  //{ATOM_DEVICE_CRTx_SUPPORT,ATOM_DEVICE_TVx_SUPPORT,ATOM_DEVICE_CVx_SUPPORT}
+  UCHAR  ucDacType;                   //{ATOM_DAC_A,ATOM_DAC_B, ATOM_EXT_DAC}
+  UCHAR  ucMisc;                                                                                       //Valid only when table revision =1.3 and above
+}DAC_LOAD_DETECTION_PARAMETERS;
 
-/*  DAC_LOAD_DETECTION_PARAMETERS.ucMisc */
+// DAC_LOAD_DETECTION_PARAMETERS.ucMisc
 #define DAC_LOAD_MISC_YPrPb                                            0x01
 
-typedef struct _DAC_LOAD_DETECTION_PS_ALLOCATION {
-       DAC_LOAD_DETECTION_PARAMETERS sDacload;
-       ULONG Reserved[2];      /*  Don't set this one, allocation for EXT DAC */
-} DAC_LOAD_DETECTION_PS_ALLOCATION;
-
-/****************************************************************************/
-/*  Structures used by DAC1EncoderControlTable.ctb and DAC2EncoderControlTable.ctb */
-/****************************************************************************/
-typedef struct _DAC_ENCODER_CONTROL_PARAMETERS {
-       USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       UCHAR ucDacStandard;    /*  See definition of ATOM_DACx_xxx, For DEC3.0, bit 7 used as internal flag to indicate DAC2 (==1) or DAC1 (==0) */
-       UCHAR ucAction;         /*  0: turn off encoder */
-       /*  1: setup and turn on encoder */
-       /*  7: ATOM_ENCODER_INIT Initialize DAC */
-} DAC_ENCODER_CONTROL_PARAMETERS;
+typedef struct _DAC_LOAD_DETECTION_PS_ALLOCATION
+{
+  DAC_LOAD_DETECTION_PARAMETERS            sDacload;
+  ULONG                                    Reserved[2];// Don't set this one, allocation for EXT DAC
+}DAC_LOAD_DETECTION_PS_ALLOCATION;
+
+/****************************************************************************/ 
+// Structures used by DAC1EncoderControlTable.ctb and DAC2EncoderControlTable.ctb
+/****************************************************************************/ 
+typedef struct _DAC_ENCODER_CONTROL_PARAMETERS 
+{
+  USHORT usPixelClock;                // in 10KHz; for bios convenient
+  UCHAR  ucDacStandard;               // See definition of ATOM_DACx_xxx, For DEC3.0, bit 7 used as internal flag to indicate DAC2 (==1) or DAC1 (==0)
+  UCHAR  ucAction;                    // 0: turn off encoder
+                                      // 1: setup and turn on encoder
+                                      // 7: ATOM_ENCODER_INIT Initialize DAC
+}DAC_ENCODER_CONTROL_PARAMETERS;
 
 #define DAC_ENCODER_CONTROL_PS_ALLOCATION  DAC_ENCODER_CONTROL_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by DIG1EncoderControlTable */
-/*                     DIG2EncoderControlTable */
-/*                     ExternalEncoderControlTable */
-/****************************************************************************/
-typedef struct _DIG_ENCODER_CONTROL_PARAMETERS {
-       USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       UCHAR ucConfig;
-       /*  [2] Link Select: */
-       /*  =0: PHY linkA if bfLane<3 */
-       /*  =1: PHY linkB if bfLanes<3 */
-       /*  =0: PHY linkA+B if bfLanes=3 */
-       /*  [3] Transmitter Sel */
-       /*  =0: UNIPHY or PCIEPHY */
-       /*  =1: LVTMA */
-       UCHAR ucAction;         /*  =0: turn off encoder */
-       /*  =1: turn on encoder */
-       UCHAR ucEncoderMode;
-       /*  =0: DP   encoder */
-       /*  =1: LVDS encoder */
-       /*  =2: DVI  encoder */
-       /*  =3: HDMI encoder */
-       /*  =4: SDVO encoder */
-       UCHAR ucLaneNum;        /*  how many lanes to enable */
-       UCHAR ucReserved[2];
-} DIG_ENCODER_CONTROL_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by DIG1EncoderControlTable
+//                    DIG2EncoderControlTable
+//                    ExternalEncoderControlTable
+/****************************************************************************/ 
+typedef struct _DIG_ENCODER_CONTROL_PARAMETERS
+{
+  USHORT usPixelClock;         // in 10KHz; for bios convenient
+  UCHAR  ucConfig;               
+                            // [2] Link Select:
+                            // =0: PHY linkA if bfLane<3
+                            // =1: PHY linkB if bfLanes<3
+                            // =0: PHY linkA+B if bfLanes=3
+                            // [3] Transmitter Sel
+                            // =0: UNIPHY or PCIEPHY
+                            // =1: LVTMA                                       
+  UCHAR ucAction;           // =0: turn off encoder                                    
+                            // =1: turn on encoder                     
+  UCHAR ucEncoderMode;
+                            // =0: DP   encoder      
+                            // =1: LVDS encoder          
+                            // =2: DVI  encoder  
+                            // =3: HDMI encoder
+                            // =4: SDVO encoder
+  UCHAR ucLaneNum;          // how many lanes to enable
+  UCHAR ucReserved[2];
+}DIG_ENCODER_CONTROL_PARAMETERS;
 #define DIG_ENCODER_CONTROL_PS_ALLOCATION                        DIG_ENCODER_CONTROL_PARAMETERS
 #define EXTERNAL_ENCODER_CONTROL_PARAMETER                     DIG_ENCODER_CONTROL_PARAMETERS
 
-/* ucConfig */
+//ucConfig
 #define ATOM_ENCODER_CONFIG_DPLINKRATE_MASK                            0x01
 #define ATOM_ENCODER_CONFIG_DPLINKRATE_1_62GHZ         0x00
 #define ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ         0x01
@@ -539,52 +593,57 @@ typedef struct _DIG_ENCODER_CONTROL_PARAMETERS {
 #define ATOM_ENCODER_CONFIG_LVTMA                                                                0x08
 #define ATOM_ENCODER_CONFIG_TRANSMITTER1                                 0x00
 #define ATOM_ENCODER_CONFIG_TRANSMITTER2                                 0x08
-#define ATOM_ENCODER_CONFIG_DIGB                                                                 0x80  /*  VBIOS Internal use, outside SW should set this bit=0 */
-/*  ucAction */
-/*  ATOM_ENABLE:  Enable Encoder */
-/*  ATOM_DISABLE: Disable Encoder */
+#define ATOM_ENCODER_CONFIG_DIGB                                                                 0x80                  // VBIOS Internal use, outside SW should set this bit=0
+// ucAction
+// ATOM_ENABLE:  Enable Encoder
+// ATOM_DISABLE: Disable Encoder
 
-/* ucEncoderMode */
+//ucEncoderMode
 #define ATOM_ENCODER_MODE_DP                                                                                   0
 #define ATOM_ENCODER_MODE_LVDS                                                                         1
 #define ATOM_ENCODER_MODE_DVI                                                                                  2
 #define ATOM_ENCODER_MODE_HDMI                                                                         3
 #define ATOM_ENCODER_MODE_SDVO                                                                         4
+#define ATOM_ENCODER_MODE_DP_AUDIO                5
 #define ATOM_ENCODER_MODE_TV                                                                                   13
 #define ATOM_ENCODER_MODE_CV                                                                                   14
 #define ATOM_ENCODER_MODE_CRT                                                                                  15
 
-typedef struct _ATOM_DIG_ENCODER_CONFIG_V2 {
+typedef struct _ATOM_DIG_ENCODER_CONFIG_V2
+{
 #if ATOM_BIG_ENDIAN
-       UCHAR ucReserved1:2;
-       UCHAR ucTransmitterSel:2;       /*  =0: UniphyAB, =1: UniphyCD  =2: UniphyEF */
-       UCHAR ucLinkSel:1;      /*  =0: linkA/C/E =1: linkB/D/F */
-       UCHAR ucReserved:1;
-       UCHAR ucDPLinkRate:1;   /*  =0: 1.62Ghz, =1: 2.7Ghz */
+    UCHAR ucReserved1:2;
+    UCHAR ucTransmitterSel:2;     // =0: UniphyAB, =1: UniphyCD  =2: UniphyEF
+    UCHAR ucLinkSel:1;            // =0: linkA/C/E =1: linkB/D/F
+    UCHAR ucReserved:1;
+    UCHAR ucDPLinkRate:1;         // =0: 1.62Ghz, =1: 2.7Ghz
 #else
-       UCHAR ucDPLinkRate:1;   /*  =0: 1.62Ghz, =1: 2.7Ghz */
-       UCHAR ucReserved:1;
-       UCHAR ucLinkSel:1;      /*  =0: linkA/C/E =1: linkB/D/F */
-       UCHAR ucTransmitterSel:2;       /*  =0: UniphyAB, =1: UniphyCD  =2: UniphyEF */
-       UCHAR ucReserved1:2;
+    UCHAR ucDPLinkRate:1;         // =0: 1.62Ghz, =1: 2.7Ghz
+    UCHAR ucReserved:1;
+    UCHAR ucLinkSel:1;            // =0: linkA/C/E =1: linkB/D/F
+    UCHAR ucTransmitterSel:2;     // =0: UniphyAB, =1: UniphyCD  =2: UniphyEF
+    UCHAR ucReserved1:2;
 #endif
-} ATOM_DIG_ENCODER_CONFIG_V2;
+}ATOM_DIG_ENCODER_CONFIG_V2;
 
-typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V2 {
-       USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       ATOM_DIG_ENCODER_CONFIG_V2 acConfig;
-       UCHAR ucAction;
-       UCHAR ucEncoderMode;
-       /*  =0: DP   encoder */
-       /*  =1: LVDS encoder */
-       /*  =2: DVI  encoder */
-       /*  =3: HDMI encoder */
-       /*  =4: SDVO encoder */
-       UCHAR ucLaneNum;        /*  how many lanes to enable */
-       UCHAR ucReserved[2];
-} DIG_ENCODER_CONTROL_PARAMETERS_V2;
 
-/* ucConfig */
+typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V2
+{
+  USHORT usPixelClock;      // in 10KHz; for bios convenient
+  ATOM_DIG_ENCODER_CONFIG_V2 acConfig;
+  UCHAR ucAction;                                       
+  UCHAR ucEncoderMode;
+                            // =0: DP   encoder      
+                            // =1: LVDS encoder          
+                            // =2: DVI  encoder  
+                            // =3: HDMI encoder
+                            // =4: SDVO encoder
+  UCHAR ucLaneNum;          // how many lanes to enable
+  UCHAR ucStatus;           // = DP_LINK_TRAINING_COMPLETE or DP_LINK_TRAINING_INCOMPLETE, only used by VBIOS with command ATOM_ENCODER_CMD_QUERY_DP_LINK_TRAINING_STATUS
+  UCHAR ucReserved;
+}DIG_ENCODER_CONTROL_PARAMETERS_V2;
+
+//ucConfig
 #define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_MASK                         0x01
 #define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_1_62GHZ                0x00
 #define ATOM_ENCODER_CONFIG_V2_DPLINKRATE_2_70GHZ                0x01
@@ -596,58 +655,122 @@ typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V2 {
 #define ATOM_ENCODER_CONFIG_V2_TRANSMITTER2                                0x08
 #define ATOM_ENCODER_CONFIG_V2_TRANSMITTER3                                0x10
 
-/****************************************************************************/
-/*  Structures used by UNIPHYTransmitterControlTable */
-/*                     LVTMATransmitterControlTable */
-/*                     DVOOutputControlTable */
-/****************************************************************************/
-typedef struct _ATOM_DP_VS_MODE {
-       UCHAR ucLaneSel;
-       UCHAR ucLaneSet;
-} ATOM_DP_VS_MODE;
-
-typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS {
-       union {
-               USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-               USHORT usInitInfo;      /*  when init uniphy,lower 8bit is used for connector type defined in objectid.h */
-               ATOM_DP_VS_MODE asMode; /*  DP Voltage swing mode */
+// ucAction:
+// ATOM_DISABLE
+// ATOM_ENABLE
+#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_START       0x08
+#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1    0x09
+#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2    0x0a
+#define ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE    0x0b
+#define ATOM_ENCODER_CMD_DP_VIDEO_OFF                 0x0c
+#define ATOM_ENCODER_CMD_DP_VIDEO_ON                  0x0d
+#define ATOM_ENCODER_CMD_QUERY_DP_LINK_TRAINING_STATUS    0x0e
+#define ATOM_ENCODER_CMD_SETUP                        0x0f
+
+// ucStatus
+#define ATOM_ENCODER_STATUS_LINK_TRAINING_COMPLETE    0x10
+#define ATOM_ENCODER_STATUS_LINK_TRAINING_INCOMPLETE  0x00
+
+// Following function ENABLE sub-function will be used by driver when TMDS/HDMI/LVDS is used, disable function will be used by driver
+typedef struct _ATOM_DIG_ENCODER_CONFIG_V3
+{
+#if ATOM_BIG_ENDIAN
+    UCHAR ucReserved1:1;
+    UCHAR ucDigSel:3;             // =0: DIGA/B/C/D/E/F
+    UCHAR ucReserved:3;
+    UCHAR ucDPLinkRate:1;         // =0: 1.62Ghz, =1: 2.7Ghz
+#else
+    UCHAR ucDPLinkRate:1;         // =0: 1.62Ghz, =1: 2.7Ghz
+    UCHAR ucReserved:3;
+    UCHAR ucDigSel:3;             // =0: DIGA/B/C/D/E/F
+    UCHAR ucReserved1:1;
+#endif
+}ATOM_DIG_ENCODER_CONFIG_V3;
+
+#define ATOM_ENCODER_CONFIG_V3_ENCODER_SEL                                       0x70
+
+
+typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V3
+{
+  USHORT usPixelClock;      // in 10KHz; for bios convenient
+  ATOM_DIG_ENCODER_CONFIG_V3 acConfig;
+  UCHAR ucAction;                              
+  UCHAR ucEncoderMode;
+                            // =0: DP   encoder      
+                            // =1: LVDS encoder          
+                            // =2: DVI  encoder  
+                            // =3: HDMI encoder
+                            // =4: SDVO encoder
+                            // =5: DP audio
+  UCHAR ucLaneNum;          // how many lanes to enable
+  UCHAR ucBitPerColor;      // only valid for DP mode when ucAction = ATOM_ENCODER_CMD_SETUP
+  UCHAR ucReserved;
+}DIG_ENCODER_CONTROL_PARAMETERS_V3;
+
+
+// define ucBitPerColor: 
+#define PANEL_BPC_UNDEFINE                               0x00
+#define PANEL_6BIT_PER_COLOR                             0x01 
+#define PANEL_8BIT_PER_COLOR                             0x02
+#define PANEL_10BIT_PER_COLOR                            0x03
+#define PANEL_12BIT_PER_COLOR                            0x04
+#define PANEL_16BIT_PER_COLOR                            0x05
+
+/****************************************************************************/ 
+// Structures used by UNIPHYTransmitterControlTable
+//                    LVTMATransmitterControlTable
+//                    DVOOutputControlTable
+/****************************************************************************/ 
+typedef struct _ATOM_DP_VS_MODE
+{
+  UCHAR ucLaneSel;
+  UCHAR ucLaneSet;
+}ATOM_DP_VS_MODE;
+
+typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS
+{
+       union
+       {
+  USHORT usPixelClock;         // in 10KHz; for bios convenient
+       USHORT usInitInfo;                      // when init uniphy,lower 8bit is used for connector type defined in objectid.h
+  ATOM_DP_VS_MODE asMode; // DP Voltage swing mode
        };
-       UCHAR ucConfig;
-       /*  [0]=0: 4 lane Link, */
-       /*     =1: 8 lane Link ( Dual Links TMDS ) */
-       /*  [1]=0: InCoherent mode */
-       /*     =1: Coherent Mode */
-       /*  [2] Link Select: */
-       /*  =0: PHY linkA   if bfLane<3 */
-       /*  =1: PHY linkB   if bfLanes<3 */
-       /*  =0: PHY linkA+B if bfLanes=3 */
-       /*  [5:4]PCIE lane Sel */
-       /*  =0: lane 0~3 or 0~7 */
-       /*  =1: lane 4~7 */
-       /*  =2: lane 8~11 or 8~15 */
-       /*  =3: lane 12~15 */
-       UCHAR ucAction;         /*  =0: turn off encoder */
-       /*  =1: turn on encoder */
-       UCHAR ucReserved[4];
-} DIG_TRANSMITTER_CONTROL_PARAMETERS;
-
-#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION          DIG_TRANSMITTER_CONTROL_PARAMETERS
-
-/* ucInitInfo */
-#define ATOM_TRAMITTER_INITINFO_CONNECTOR_MASK 0x00ff
-
-/* ucConfig */
+  UCHAR ucConfig;
+                                                                                                       // [0]=0: 4 lane Link,      
+                                                                                                       //    =1: 8 lane Link ( Dual Links TMDS ) 
+                          // [1]=0: InCoherent mode   
+                                                                                                       //    =1: Coherent Mode                                                                         
+                                                                                                       // [2] Link Select:
+                                                                                               // =0: PHY linkA   if bfLane<3
+                                                                                                       // =1: PHY linkB   if bfLanes<3
+                                                                                               // =0: PHY linkA+B if bfLanes=3         
+                          // [5:4]PCIE lane Sel
+                          // =0: lane 0~3 or 0~7
+                          // =1: lane 4~7
+                          // =2: lane 8~11 or 8~15
+                          // =3: lane 12~15 
+       UCHAR ucAction;                           // =0: turn off encoder                                       
+                               // =1: turn on encoder                  
+  UCHAR ucReserved[4];
+}DIG_TRANSMITTER_CONTROL_PARAMETERS;
+
+#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION          DIG_TRANSMITTER_CONTROL_PARAMETERS                                      
+
+//ucInitInfo
+#define ATOM_TRAMITTER_INITINFO_CONNECTOR_MASK 0x00ff                  
+
+//ucConfig 
 #define ATOM_TRANSMITTER_CONFIG_8LANE_LINK                     0x01
 #define ATOM_TRANSMITTER_CONFIG_COHERENT                               0x02
 #define ATOM_TRANSMITTER_CONFIG_LINK_SEL_MASK          0x04
 #define ATOM_TRANSMITTER_CONFIG_LINKA                                          0x00
 #define ATOM_TRANSMITTER_CONFIG_LINKB                                          0x04
-#define ATOM_TRANSMITTER_CONFIG_LINKA_B                                        0x00
+#define ATOM_TRANSMITTER_CONFIG_LINKA_B                                        0x00                    
 #define ATOM_TRANSMITTER_CONFIG_LINKB_A                                        0x04
 
-#define ATOM_TRANSMITTER_CONFIG_ENCODER_SEL_MASK       0x08    /*  only used when ATOM_TRANSMITTER_ACTION_ENABLE */
-#define ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER           0x00    /*  only used when ATOM_TRANSMITTER_ACTION_ENABLE */
-#define ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER           0x08    /*  only used when ATOM_TRANSMITTER_ACTION_ENABLE */
+#define ATOM_TRANSMITTER_CONFIG_ENCODER_SEL_MASK       0x08                    // only used when ATOM_TRANSMITTER_ACTION_ENABLE
+#define ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER           0x00                            // only used when ATOM_TRANSMITTER_ACTION_ENABLE
+#define ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER           0x08                            // only used when ATOM_TRANSMITTER_ACTION_ENABLE
 
 #define ATOM_TRANSMITTER_CONFIG_CLKSRC_MASK                    0x30
 #define ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL                    0x00
@@ -661,7 +784,7 @@ typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS {
 #define ATOM_TRANSMITTER_CONFIG_LANE_8_15                              0x80
 #define ATOM_TRANSMITTER_CONFIG_LANE_12_15                     0xc0
 
-/* ucAction */
+//ucAction
 #define ATOM_TRANSMITTER_ACTION_DISABLE                                               0
 #define ATOM_TRANSMITTER_ACTION_ENABLE                                        1
 #define ATOM_TRANSMITTER_ACTION_LCD_BLOFF                                     2
@@ -674,93 +797,168 @@ typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS {
 #define ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT                 9
 #define ATOM_TRANSMITTER_ACTION_SETUP                                                 10
 #define ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH           11
+#define ATOM_TRANSMITTER_ACTION_POWER_ON               12
+#define ATOM_TRANSMITTER_ACTION_POWER_OFF              13
 
-/*  Following are used for DigTransmitterControlTable ver1.2 */
-typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V2 {
+// Following are used for DigTransmitterControlTable ver1.2
+typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V2
+{
 #if ATOM_BIG_ENDIAN
-       UCHAR ucTransmitterSel:2;       /* bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) */
-       /*         =1 Dig Transmitter 2 ( Uniphy CD ) */
-       /*         =2 Dig Transmitter 3 ( Uniphy EF ) */
-       UCHAR ucReserved:1;
-       UCHAR fDPConnector:1;   /* bit4=0: DP connector  =1: None DP connector */
-       UCHAR ucEncoderSel:1;   /* bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 ) */
-       UCHAR ucLinkSel:1;      /* bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E */
-       /*     =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F */
-
-       UCHAR fCoherentMode:1;  /* bit1=1: Coherent Mode ( for DVI/HDMI mode ) */
-       UCHAR fDualLinkConnector:1;     /* bit0=1: Dual Link DVI connector */
+  UCHAR ucTransmitterSel:2;         //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB )
+                                    //        =1 Dig Transmitter 2 ( Uniphy CD )
+                                    //        =2 Dig Transmitter 3 ( Uniphy EF )
+  UCHAR ucReserved:1;               
+  UCHAR fDPConnector:1;             //bit4=0: DP connector  =1: None DP connector
+  UCHAR ucEncoderSel:1;             //bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 )
+  UCHAR ucLinkSel:1;                //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E
+                                    //    =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F
+
+  UCHAR fCoherentMode:1;            //bit1=1: Coherent Mode ( for DVI/HDMI mode )
+  UCHAR fDualLinkConnector:1;       //bit0=1: Dual Link DVI connector
 #else
-       UCHAR fDualLinkConnector:1;     /* bit0=1: Dual Link DVI connector */
-       UCHAR fCoherentMode:1;  /* bit1=1: Coherent Mode ( for DVI/HDMI mode ) */
-       UCHAR ucLinkSel:1;      /* bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E */
-       /*     =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F */
-       UCHAR ucEncoderSel:1;   /* bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 ) */
-       UCHAR fDPConnector:1;   /* bit4=0: DP connector  =1: None DP connector */
-       UCHAR ucReserved:1;
-       UCHAR ucTransmitterSel:2;       /* bit7:6: =0 Dig Transmitter 1 ( Uniphy AB ) */
-       /*         =1 Dig Transmitter 2 ( Uniphy CD ) */
-       /*         =2 Dig Transmitter 3 ( Uniphy EF ) */
+  UCHAR fDualLinkConnector:1;       //bit0=1: Dual Link DVI connector
+  UCHAR fCoherentMode:1;            //bit1=1: Coherent Mode ( for DVI/HDMI mode )
+  UCHAR ucLinkSel:1;                //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E
+                                    //    =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F
+  UCHAR ucEncoderSel:1;             //bit3=0: Data/Clk path source from DIGA( DIG inst0 ). =1: Data/clk path source from DIGB ( DIG inst1 )
+  UCHAR fDPConnector:1;             //bit4=0: DP connector  =1: None DP connector
+  UCHAR ucReserved:1;               
+  UCHAR ucTransmitterSel:2;         //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB )
+                                    //        =1 Dig Transmitter 2 ( Uniphy CD )
+                                    //        =2 Dig Transmitter 3 ( Uniphy EF )
 #endif
-} ATOM_DIG_TRANSMITTER_CONFIG_V2;
+}ATOM_DIG_TRANSMITTER_CONFIG_V2;
 
-/* ucConfig */
-/* Bit0 */
+//ucConfig 
+//Bit0
 #define ATOM_TRANSMITTER_CONFIG_V2_DUAL_LINK_CONNECTOR                 0x01
 
-/* Bit1 */
+//Bit1
 #define ATOM_TRANSMITTER_CONFIG_V2_COHERENT                                      0x02
 
-/* Bit2 */
+//Bit2
 #define ATOM_TRANSMITTER_CONFIG_V2_LINK_SEL_MASK                       0x04
-#define ATOM_TRANSMITTER_CONFIG_V2_LINKA                                   0x00
+#define ATOM_TRANSMITTER_CONFIG_V2_LINKA                                   0x00
 #define ATOM_TRANSMITTER_CONFIG_V2_LINKB                                           0x04
 
-/*  Bit3 */
+// Bit3
 #define ATOM_TRANSMITTER_CONFIG_V2_ENCODER_SEL_MASK            0x08
-#define ATOM_TRANSMITTER_CONFIG_V2_DIG1_ENCODER                          0x00  /*  only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP */
-#define ATOM_TRANSMITTER_CONFIG_V2_DIG2_ENCODER                          0x08  /*  only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP */
+#define ATOM_TRANSMITTER_CONFIG_V2_DIG1_ENCODER                          0x00                          // only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP
+#define ATOM_TRANSMITTER_CONFIG_V2_DIG2_ENCODER                          0x08                          // only used when ucAction == ATOM_TRANSMITTER_ACTION_ENABLE or ATOM_TRANSMITTER_ACTION_SETUP
 
-/*  Bit4 */
+// Bit4
 #define ATOM_TRASMITTER_CONFIG_V2_DP_CONNECTOR                         0x10
 
-/*  Bit7:6 */
+// Bit7:6
 #define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER_SEL_MASK     0xC0
-#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER1                        0x00    /* AB */
-#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER2                        0x40    /* CD */
-#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER3                        0x80    /* EF */
-
-typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 {
-       union {
-               USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-               USHORT usInitInfo;      /*  when init uniphy,lower 8bit is used for connector type defined in objectid.h */
-               ATOM_DP_VS_MODE asMode; /*  DP Voltage swing mode */
+#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER1                0x00    //AB
+#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER2                0x40    //CD
+#define ATOM_TRANSMITTER_CONFIG_V2_TRANSMITTER3                0x80    //EF
+
+typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V2
+{
+       union
+       {
+  USHORT usPixelClock;         // in 10KHz; for bios convenient
+       USHORT usInitInfo;                      // when init uniphy,lower 8bit is used for connector type defined in objectid.h
+  ATOM_DP_VS_MODE asMode; // DP Voltage swing mode
        };
-       ATOM_DIG_TRANSMITTER_CONFIG_V2 acConfig;
-       UCHAR ucAction;         /*  define as ATOM_TRANSMITER_ACTION_XXX */
-       UCHAR ucReserved[4];
-} DIG_TRANSMITTER_CONTROL_PARAMETERS_V2;
+  ATOM_DIG_TRANSMITTER_CONFIG_V2 acConfig;
+       UCHAR ucAction;                           // define as ATOM_TRANSMITER_ACTION_XXX
+  UCHAR ucReserved[4];
+}DIG_TRANSMITTER_CONTROL_PARAMETERS_V2;
 
-/****************************************************************************/
-/*  Structures used by DAC1OuputControlTable */
-/*                     DAC2OuputControlTable */
-/*                     LVTMAOutputControlTable  (Before DEC30) */
-/*                     TMDSAOutputControlTable  (Before DEC30) */
-/****************************************************************************/
-typedef struct _DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS {
-       UCHAR ucAction;         /*  Possible input:ATOM_ENABLE||ATOMDISABLE */
-       /*  When the display is LCD, in addition to above: */
-       /*  ATOM_LCD_BLOFF|| ATOM_LCD_BLON ||ATOM_LCD_BL_BRIGHTNESS_CONTROL||ATOM_LCD_SELFTEST_START|| */
-       /*  ATOM_LCD_SELFTEST_STOP */
+typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V3
+{
+#if ATOM_BIG_ENDIAN
+  UCHAR ucTransmitterSel:2;         //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB )
+                                    //        =1 Dig Transmitter 2 ( Uniphy CD )
+                                    //        =2 Dig Transmitter 3 ( Uniphy EF )
+  UCHAR ucRefClkSource:2;           //bit5:4: PPLL1 =0, PPLL2=1, EXT_CLK=2
+  UCHAR ucEncoderSel:1;             //bit3=0: Data/Clk path source from DIGA/C/E. =1: Data/clk path source from DIGB/D/F
+  UCHAR ucLinkSel:1;                //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E
+                                    //    =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F
+  UCHAR fCoherentMode:1;            //bit1=1: Coherent Mode ( for DVI/HDMI mode )
+  UCHAR fDualLinkConnector:1;       //bit0=1: Dual Link DVI connector
+#else
+  UCHAR fDualLinkConnector:1;       //bit0=1: Dual Link DVI connector
+  UCHAR fCoherentMode:1;            //bit1=1: Coherent Mode ( for DVI/HDMI mode )
+  UCHAR ucLinkSel:1;                //bit2=0: Uniphy LINKA or C or E when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is A or C or E
+                                    //    =1: Uniphy LINKB or D or F when fDualLinkConnector=0. when fDualLinkConnector=1, it means master link of dual link is B or D or F
+  UCHAR ucEncoderSel:1;             //bit3=0: Data/Clk path source from DIGA/C/E. =1: Data/clk path source from DIGB/D/F
+  UCHAR ucRefClkSource:2;           //bit5:4: PPLL1 =0, PPLL2=1, EXT_CLK=2
+  UCHAR ucTransmitterSel:2;         //bit7:6: =0 Dig Transmitter 1 ( Uniphy AB )
+                                    //        =1 Dig Transmitter 2 ( Uniphy CD )
+                                    //        =2 Dig Transmitter 3 ( Uniphy EF )
+#endif
+}ATOM_DIG_TRANSMITTER_CONFIG_V3;
 
-       UCHAR aucPadding[3];    /*  padding to DWORD aligned */
-} DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS;
+typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V3
+{
+       union
+       {
+    USHORT usPixelClock;               // in 10KHz; for bios convenient
+         USHORT usInitInfo;                    // when init uniphy,lower 8bit is used for connector type defined in objectid.h
+    ATOM_DP_VS_MODE asMode; // DP Voltage swing mode
+       };
+  ATOM_DIG_TRANSMITTER_CONFIG_V3 acConfig;
+       UCHAR ucAction;                             // define as ATOM_TRANSMITER_ACTION_XXX
+  UCHAR ucLaneNum;
+  UCHAR ucReserved[3];
+}DIG_TRANSMITTER_CONTROL_PARAMETERS_V3;
+
+//ucConfig 
+//Bit0
+#define ATOM_TRANSMITTER_CONFIG_V3_DUAL_LINK_CONNECTOR                 0x01
+
+//Bit1
+#define ATOM_TRANSMITTER_CONFIG_V3_COHERENT                                      0x02
+
+//Bit2
+#define ATOM_TRANSMITTER_CONFIG_V3_LINK_SEL_MASK                       0x04
+#define ATOM_TRANSMITTER_CONFIG_V3_LINKA                                   0x00
+#define ATOM_TRANSMITTER_CONFIG_V3_LINKB                                           0x04
+
+// Bit3
+#define ATOM_TRANSMITTER_CONFIG_V3_ENCODER_SEL_MASK            0x08
+#define ATOM_TRANSMITTER_CONFIG_V3_DIG1_ENCODER                          0x00
+#define ATOM_TRANSMITTER_CONFIG_V3_DIG2_ENCODER                          0x08
+
+// Bit5:4
+#define ATOM_TRASMITTER_CONFIG_V3_REFCLK_SEL_MASK              0x30
+#define ATOM_TRASMITTER_CONFIG_V3_P1PLL                                0x00
+#define ATOM_TRASMITTER_CONFIG_V3_P2PLL                                  0x10
+#define ATOM_TRASMITTER_CONFIG_V3_REFCLK_SRC_EXT            0x20
+
+// Bit7:6
+#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER_SEL_MASK     0xC0
+#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER1                0x00    //AB
+#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER2                0x40    //CD
+#define ATOM_TRANSMITTER_CONFIG_V3_TRANSMITTER3                0x80    //EF
+
+/****************************************************************************/ 
+// Structures used by DAC1OuputControlTable
+//                    DAC2OuputControlTable
+//                    LVTMAOutputControlTable  (Before DEC30)
+//                    TMDSAOutputControlTable  (Before DEC30)
+/****************************************************************************/ 
+typedef struct _DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS
+{
+  UCHAR  ucAction;                    // Possible input:ATOM_ENABLE||ATOMDISABLE
+                                      // When the display is LCD, in addition to above:
+                                      // ATOM_LCD_BLOFF|| ATOM_LCD_BLON ||ATOM_LCD_BL_BRIGHTNESS_CONTROL||ATOM_LCD_SELFTEST_START||
+                                      // ATOM_LCD_SELFTEST_STOP
+                                      
+  UCHAR  aucPadding[3];               // padding to DWORD aligned
+}DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS;
 
 #define DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS
 
-#define CRT1_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS
+
+#define CRT1_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS 
 #define CRT1_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION
 
-#define CRT2_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS
+#define CRT2_OUTPUT_CONTROL_PARAMETERS     DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS 
 #define CRT2_OUTPUT_CONTROL_PS_ALLOCATION  DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION
 
 #define CV1_OUTPUT_CONTROL_PARAMETERS      DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS
@@ -782,397 +980,550 @@ typedef struct _DISPLAY_DEVICE_OUTPUT_CONTROL_PARAMETERS {
 #define DVO_OUTPUT_CONTROL_PS_ALLOCATION   DIG_TRANSMITTER_CONTROL_PS_ALLOCATION
 #define DVO_OUTPUT_CONTROL_PARAMETERS_V3        DIG_TRANSMITTER_CONTROL_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by BlankCRTCTable */
-/****************************************************************************/
-typedef struct _BLANK_CRTC_PARAMETERS {
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucBlanking;       /*  ATOM_BLANKING or ATOM_BLANKINGOFF */
-       USHORT usBlackColorRCr;
-       USHORT usBlackColorGY;
-       USHORT usBlackColorBCb;
-} BLANK_CRTC_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by BlankCRTCTable
+/****************************************************************************/ 
+typedef struct _BLANK_CRTC_PARAMETERS
+{
+  UCHAR  ucCRTC;                       // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR  ucBlanking;                  // ATOM_BLANKING or ATOM_BLANKINGOFF
+  USHORT usBlackColorRCr;
+  USHORT usBlackColorGY;
+  USHORT usBlackColorBCb;
+}BLANK_CRTC_PARAMETERS;
 #define BLANK_CRTC_PS_ALLOCATION    BLANK_CRTC_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by EnableCRTCTable */
-/*                     EnableCRTCMemReqTable */
-/*                     UpdateCRTC_DoubleBufferRegistersTable */
-/****************************************************************************/
-typedef struct _ENABLE_CRTC_PARAMETERS {
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucEnable;         /*  ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucPadding[2];
-} ENABLE_CRTC_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by EnableCRTCTable
+//                    EnableCRTCMemReqTable
+//                    UpdateCRTC_DoubleBufferRegistersTable
+/****************************************************************************/ 
+typedef struct _ENABLE_CRTC_PARAMETERS
+{
+  UCHAR ucCRTC;                          // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR ucEnable;                     // ATOM_ENABLE or ATOM_DISABLE 
+  UCHAR ucPadding[2];
+}ENABLE_CRTC_PARAMETERS;
 #define ENABLE_CRTC_PS_ALLOCATION   ENABLE_CRTC_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by SetCRTC_OverScanTable */
-/****************************************************************************/
-typedef struct _SET_CRTC_OVERSCAN_PARAMETERS {
-       USHORT usOverscanRight; /*  right */
-       USHORT usOverscanLeft;  /*  left */
-       USHORT usOverscanBottom;        /*  bottom */
-       USHORT usOverscanTop;   /*  top */
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucPadding[3];
-} SET_CRTC_OVERSCAN_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by SetCRTC_OverScanTable
+/****************************************************************************/ 
+typedef struct _SET_CRTC_OVERSCAN_PARAMETERS
+{
+  USHORT usOverscanRight;             // right
+  USHORT usOverscanLeft;              // left
+  USHORT usOverscanBottom;            // bottom
+  USHORT usOverscanTop;               // top
+  UCHAR  ucCRTC;                      // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR  ucPadding[3];
+}SET_CRTC_OVERSCAN_PARAMETERS;
 #define SET_CRTC_OVERSCAN_PS_ALLOCATION  SET_CRTC_OVERSCAN_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by SetCRTC_ReplicationTable */
-/****************************************************************************/
-typedef struct _SET_CRTC_REPLICATION_PARAMETERS {
-       UCHAR ucH_Replication;  /*  horizontal replication */
-       UCHAR ucV_Replication;  /*  vertical replication */
-       UCHAR usCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucPadding;
-} SET_CRTC_REPLICATION_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by SetCRTC_ReplicationTable
+/****************************************************************************/ 
+typedef struct _SET_CRTC_REPLICATION_PARAMETERS
+{
+  UCHAR ucH_Replication;              // horizontal replication
+  UCHAR ucV_Replication;              // vertical replication
+  UCHAR usCRTC;                       // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR ucPadding;
+}SET_CRTC_REPLICATION_PARAMETERS;
 #define SET_CRTC_REPLICATION_PS_ALLOCATION  SET_CRTC_REPLICATION_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by SelectCRTC_SourceTable */
-/****************************************************************************/
-typedef struct _SELECT_CRTC_SOURCE_PARAMETERS {
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucDevice;         /*  ATOM_DEVICE_CRT1|ATOM_DEVICE_CRT2|.... */
-       UCHAR ucPadding[2];
-} SELECT_CRTC_SOURCE_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by SelectCRTC_SourceTable
+/****************************************************************************/ 
+typedef struct _SELECT_CRTC_SOURCE_PARAMETERS
+{
+  UCHAR ucCRTC;                          // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR ucDevice;                     // ATOM_DEVICE_CRT1|ATOM_DEVICE_CRT2|....
+  UCHAR ucPadding[2];
+}SELECT_CRTC_SOURCE_PARAMETERS;
 #define SELECT_CRTC_SOURCE_PS_ALLOCATION  SELECT_CRTC_SOURCE_PARAMETERS
 
-typedef struct _SELECT_CRTC_SOURCE_PARAMETERS_V2 {
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucEncoderID;      /*  DAC1/DAC2/TVOUT/DIG1/DIG2/DVO */
-       UCHAR ucEncodeMode;     /*  Encoding mode, only valid when using DIG1/DIG2/DVO */
-       UCHAR ucPadding;
-} SELECT_CRTC_SOURCE_PARAMETERS_V2;
-
-/* ucEncoderID */
-/* #define ASIC_INT_DAC1_ENCODER_ID                                              0x00 */
-/* #define ASIC_INT_TV_ENCODER_ID                                                                        0x02 */
-/* #define ASIC_INT_DIG1_ENCODER_ID                                                              0x03 */
-/* #define ASIC_INT_DAC2_ENCODER_ID                                                              0x04 */
-/* #define ASIC_EXT_TV_ENCODER_ID                                                                        0x06 */
-/* #define ASIC_INT_DVO_ENCODER_ID                                                                       0x07 */
-/* #define ASIC_INT_DIG2_ENCODER_ID                                                              0x09 */
-/* #define ASIC_EXT_DIG_ENCODER_ID                                                                       0x05 */
-
-/* ucEncodeMode */
-/* #define ATOM_ENCODER_MODE_DP                                                                          0 */
-/* #define ATOM_ENCODER_MODE_LVDS                                                                        1 */
-/* #define ATOM_ENCODER_MODE_DVI                                                                         2 */
-/* #define ATOM_ENCODER_MODE_HDMI                                                                        3 */
-/* #define ATOM_ENCODER_MODE_SDVO                                                                        4 */
-/* #define ATOM_ENCODER_MODE_TV                                                                          13 */
-/* #define ATOM_ENCODER_MODE_CV                                                                          14 */
-/* #define ATOM_ENCODER_MODE_CRT                                                                         15 */
-
-/****************************************************************************/
-/*  Structures used by SetPixelClockTable */
-/*                     GetPixelClockTable */
-/****************************************************************************/
-/* Major revision=1., Minor revision=1 */
-typedef struct _PIXEL_CLOCK_PARAMETERS {
-       USHORT usPixelClock;    /*  in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) */
-       /*  0 means disable PPLL */
-       USHORT usRefDiv;        /*  Reference divider */
-       USHORT usFbDiv;         /*  feedback divider */
-       UCHAR ucPostDiv;        /*  post divider */
-       UCHAR ucFracFbDiv;      /*  fractional feedback divider */
-       UCHAR ucPpll;           /*  ATOM_PPLL1 or ATOM_PPL2 */
-       UCHAR ucRefDivSrc;      /*  ATOM_PJITTER or ATO_NONPJITTER */
-       UCHAR ucCRTC;           /*  Which CRTC uses this Ppll */
-       UCHAR ucPadding;
-} PIXEL_CLOCK_PARAMETERS;
-
-/* Major revision=1., Minor revision=2, add ucMiscIfno */
-/* ucMiscInfo: */
+typedef struct _SELECT_CRTC_SOURCE_PARAMETERS_V2
+{
+  UCHAR ucCRTC;                          // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR ucEncoderID;                  // DAC1/DAC2/TVOUT/DIG1/DIG2/DVO
+  UCHAR ucEncodeMode;                                                                  // Encoding mode, only valid when using DIG1/DIG2/DVO
+  UCHAR ucPadding;
+}SELECT_CRTC_SOURCE_PARAMETERS_V2;
+
+//ucEncoderID
+//#define ASIC_INT_DAC1_ENCODER_ID                                             0x00 
+//#define ASIC_INT_TV_ENCODER_ID                                                                       0x02
+//#define ASIC_INT_DIG1_ENCODER_ID                                                             0x03
+//#define ASIC_INT_DAC2_ENCODER_ID                                                             0x04
+//#define ASIC_EXT_TV_ENCODER_ID                                                                       0x06
+//#define ASIC_INT_DVO_ENCODER_ID                                                                      0x07
+//#define ASIC_INT_DIG2_ENCODER_ID                                                             0x09
+//#define ASIC_EXT_DIG_ENCODER_ID                                                                      0x05
+
+//ucEncodeMode
+//#define ATOM_ENCODER_MODE_DP                                                                         0
+//#define ATOM_ENCODER_MODE_LVDS                                                                       1
+//#define ATOM_ENCODER_MODE_DVI                                                                                2
+//#define ATOM_ENCODER_MODE_HDMI                                                                       3
+//#define ATOM_ENCODER_MODE_SDVO                                                                       4
+//#define ATOM_ENCODER_MODE_TV                                                                         13
+//#define ATOM_ENCODER_MODE_CV                                                                         14
+//#define ATOM_ENCODER_MODE_CRT                                                                                15
+
+/****************************************************************************/ 
+// Structures used by SetPixelClockTable
+//                    GetPixelClockTable 
+/****************************************************************************/ 
+//Major revision=1., Minor revision=1
+typedef struct _PIXEL_CLOCK_PARAMETERS
+{
+  USHORT usPixelClock;                // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div)
+                                      // 0 means disable PPLL
+  USHORT usRefDiv;                    // Reference divider
+  USHORT usFbDiv;                     // feedback divider
+  UCHAR  ucPostDiv;                   // post divider  
+  UCHAR  ucFracFbDiv;                 // fractional feedback divider
+  UCHAR  ucPpll;                      // ATOM_PPLL1 or ATOM_PPL2
+  UCHAR  ucRefDivSrc;                 // ATOM_PJITTER or ATO_NONPJITTER
+  UCHAR  ucCRTC;                      // Which CRTC uses this Ppll
+  UCHAR  ucPadding;
+}PIXEL_CLOCK_PARAMETERS;
+
+//Major revision=1., Minor revision=2, add ucMiscIfno
+//ucMiscInfo:
 #define MISC_FORCE_REPROG_PIXEL_CLOCK 0x1
 #define MISC_DEVICE_INDEX_MASK        0xF0
 #define MISC_DEVICE_INDEX_SHIFT       4
 
-typedef struct _PIXEL_CLOCK_PARAMETERS_V2 {
-       USHORT usPixelClock;    /*  in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) */
-       /*  0 means disable PPLL */
-       USHORT usRefDiv;        /*  Reference divider */
-       USHORT usFbDiv;         /*  feedback divider */
-       UCHAR ucPostDiv;        /*  post divider */
-       UCHAR ucFracFbDiv;      /*  fractional feedback divider */
-       UCHAR ucPpll;           /*  ATOM_PPLL1 or ATOM_PPL2 */
-       UCHAR ucRefDivSrc;      /*  ATOM_PJITTER or ATO_NONPJITTER */
-       UCHAR ucCRTC;           /*  Which CRTC uses this Ppll */
-       UCHAR ucMiscInfo;       /*  Different bits for different purpose, bit [7:4] as device index, bit[0]=Force prog */
-} PIXEL_CLOCK_PARAMETERS_V2;
-
-/* Major revision=1., Minor revision=3, structure/definition change */
-/* ucEncoderMode: */
-/* ATOM_ENCODER_MODE_DP */
-/* ATOM_ENOCDER_MODE_LVDS */
-/* ATOM_ENOCDER_MODE_DVI */
-/* ATOM_ENOCDER_MODE_HDMI */
-/* ATOM_ENOCDER_MODE_SDVO */
-/* ATOM_ENCODER_MODE_TV                                                                          13 */
-/* ATOM_ENCODER_MODE_CV                                                                          14 */
-/* ATOM_ENCODER_MODE_CRT                                                                         15 */
-
-/* ucDVOConfig */
-/* #define DVO_ENCODER_CONFIG_RATE_SEL                                                   0x01 */
-/* #define DVO_ENCODER_CONFIG_DDR_SPEED                                          0x00 */
-/* #define DVO_ENCODER_CONFIG_SDR_SPEED                                          0x01 */
-/* #define DVO_ENCODER_CONFIG_OUTPUT_SEL                                         0x0c */
-/* #define DVO_ENCODER_CONFIG_LOW12BIT                                                   0x00 */
-/* #define DVO_ENCODER_CONFIG_UPPER12BIT                                         0x04 */
-/* #define DVO_ENCODER_CONFIG_24BIT                                                              0x08 */
-
-/* ucMiscInfo: also changed, see below */
+typedef struct _PIXEL_CLOCK_PARAMETERS_V2
+{
+  USHORT usPixelClock;                // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div)
+                                      // 0 means disable PPLL
+  USHORT usRefDiv;                    // Reference divider
+  USHORT usFbDiv;                     // feedback divider
+  UCHAR  ucPostDiv;                   // post divider  
+  UCHAR  ucFracFbDiv;                 // fractional feedback divider
+  UCHAR  ucPpll;                      // ATOM_PPLL1 or ATOM_PPL2
+  UCHAR  ucRefDivSrc;                 // ATOM_PJITTER or ATO_NONPJITTER
+  UCHAR  ucCRTC;                      // Which CRTC uses this Ppll
+  UCHAR  ucMiscInfo;                  // Different bits for different purpose, bit [7:4] as device index, bit[0]=Force prog
+}PIXEL_CLOCK_PARAMETERS_V2;
+
+//Major revision=1., Minor revision=3, structure/definition change
+//ucEncoderMode:
+//ATOM_ENCODER_MODE_DP
+//ATOM_ENOCDER_MODE_LVDS
+//ATOM_ENOCDER_MODE_DVI
+//ATOM_ENOCDER_MODE_HDMI
+//ATOM_ENOCDER_MODE_SDVO
+//ATOM_ENCODER_MODE_TV                                                                         13
+//ATOM_ENCODER_MODE_CV                                                                         14
+//ATOM_ENCODER_MODE_CRT                                                                                15
+
+//ucDVOConfig
+//#define DVO_ENCODER_CONFIG_RATE_SEL                                                  0x01
+//#define DVO_ENCODER_CONFIG_DDR_SPEED                                         0x00
+//#define DVO_ENCODER_CONFIG_SDR_SPEED                                         0x01
+//#define DVO_ENCODER_CONFIG_OUTPUT_SEL                                                0x0c
+//#define DVO_ENCODER_CONFIG_LOW12BIT                                                  0x00
+//#define DVO_ENCODER_CONFIG_UPPER12BIT                                                0x04
+//#define DVO_ENCODER_CONFIG_24BIT                                                             0x08
+
+//ucMiscInfo: also changed, see below
 #define PIXEL_CLOCK_MISC_FORCE_PROG_PPLL                                               0x01
 #define PIXEL_CLOCK_MISC_VGA_MODE                                                                              0x02
 #define PIXEL_CLOCK_MISC_CRTC_SEL_MASK                                                 0x04
 #define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC1                                                        0x00
 #define PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2                                                        0x04
 #define PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK                        0x08
+#define PIXEL_CLOCK_MISC_REF_DIV_SRC                    0x10
+// V1.4 for RoadRunner
+#define PIXEL_CLOCK_V4_MISC_SS_ENABLE               0x10
+#define PIXEL_CLOCK_V4_MISC_COHERENT_MODE           0x20
 
-typedef struct _PIXEL_CLOCK_PARAMETERS_V3 {
-       USHORT usPixelClock;    /*  in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div) */
-       /*  0 means disable PPLL. For VGA PPLL,make sure this value is not 0. */
-       USHORT usRefDiv;        /*  Reference divider */
-       USHORT usFbDiv;         /*  feedback divider */
-       UCHAR ucPostDiv;        /*  post divider */
-       UCHAR ucFracFbDiv;      /*  fractional feedback divider */
-       UCHAR ucPpll;           /*  ATOM_PPLL1 or ATOM_PPL2 */
-       UCHAR ucTransmitterId;  /*  graphic encoder id defined in objectId.h */
-       union {
-               UCHAR ucEncoderMode;    /*  encoder type defined as ATOM_ENCODER_MODE_DP/DVI/HDMI/ */
-               UCHAR ucDVOConfig;      /*  when use DVO, need to know SDR/DDR, 12bit or 24bit */
+typedef struct _PIXEL_CLOCK_PARAMETERS_V3
+{
+  USHORT usPixelClock;                // in 10kHz unit; for bios convenient = (RefClk*FB_Div)/(Ref_Div*Post_Div)
+                                      // 0 means disable PPLL. For VGA PPLL,make sure this value is not 0.
+  USHORT usRefDiv;                    // Reference divider
+  USHORT usFbDiv;                     // feedback divider
+  UCHAR  ucPostDiv;                   // post divider  
+  UCHAR  ucFracFbDiv;                 // fractional feedback divider
+  UCHAR  ucPpll;                      // ATOM_PPLL1 or ATOM_PPL2
+  UCHAR  ucTransmitterId;             // graphic encoder id defined in objectId.h
+       union
+       {
+  UCHAR  ucEncoderMode;               // encoder type defined as ATOM_ENCODER_MODE_DP/DVI/HDMI/
+       UCHAR  ucDVOConfig;                                                                     // when use DVO, need to know SDR/DDR, 12bit or 24bit
        };
-       UCHAR ucMiscInfo;       /*  bit[0]=Force program, bit[1]= set pclk for VGA, b[2]= CRTC sel */
-       /*  bit[3]=0:use PPLL for dispclk source, =1: use engine clock for dispclock source */
-} PIXEL_CLOCK_PARAMETERS_V3;
+  UCHAR  ucMiscInfo;                  // bit[0]=Force program, bit[1]= set pclk for VGA, b[2]= CRTC sel
+                                      // bit[3]=0:use PPLL for dispclk source, =1: use engine clock for dispclock source
+                                      // bit[4]=0:use XTALIN as the source of reference divider,=1 use the pre-defined clock as the source of reference divider
+}PIXEL_CLOCK_PARAMETERS_V3;
 
 #define PIXEL_CLOCK_PARAMETERS_LAST                    PIXEL_CLOCK_PARAMETERS_V2
 #define GET_PIXEL_CLOCK_PS_ALLOCATION          PIXEL_CLOCK_PARAMETERS_LAST
 
-/****************************************************************************/
-/*  Structures used by AdjustDisplayPllTable */
-/****************************************************************************/
-typedef struct _ADJUST_DISPLAY_PLL_PARAMETERS {
+typedef struct _PIXEL_CLOCK_PARAMETERS_V5
+{
+  UCHAR  ucCRTC;             // ATOM_CRTC1~6, indicate the CRTC controller to 
+                             // drive the pixel clock. not used for DCPLL case.
+  union{
+  UCHAR  ucReserved;
+  UCHAR  ucFracFbDiv;        // [gphan] temporary to prevent build problem.  remove it after driver code is changed.
+  };
+  USHORT usPixelClock;       // target the pixel clock to drive the CRTC timing
+                             // 0 means disable PPLL/DCPLL. 
+  USHORT usFbDiv;            // feedback divider integer part. 
+  UCHAR  ucPostDiv;          // post divider. 
+  UCHAR  ucRefDiv;           // Reference divider
+  UCHAR  ucPpll;             // ATOM_PPLL1/ATOM_PPLL2/ATOM_DCPLL
+  UCHAR  ucTransmitterID;    // ASIC encoder id defined in objectId.h, 
+                             // indicate which graphic encoder will be used. 
+  UCHAR  ucEncoderMode;      // Encoder mode: 
+  UCHAR  ucMiscInfo;         // bit[0]= Force program PPLL 
+                             // bit[1]= when VGA timing is used. 
+                             // bit[3:2]= HDMI panel bit depth: =0: 24bpp =1:30bpp, =2:32bpp
+                             // bit[4]= RefClock source for PPLL. 
+                             // =0: XTLAIN( default mode )
+                                  // =1: other external clock source, which is pre-defined
+                             //     by VBIOS depend on the feature required.
+                             // bit[7:5]: reserved.
+  ULONG  ulFbDivDecFrac;     // 20 bit feedback divider decimal fraction part, range from 1~999999 ( 0.000001 to 0.999999 )
+
+}PIXEL_CLOCK_PARAMETERS_V5;
+
+#define PIXEL_CLOCK_V5_MISC_FORCE_PROG_PPLL                                    0x01
+#define PIXEL_CLOCK_V5_MISC_VGA_MODE                                                           0x02
+#define PIXEL_CLOCK_V5_MISC_HDMI_BPP_MASK           0x0c
+#define PIXEL_CLOCK_V5_MISC_HDMI_24BPP              0x00
+#define PIXEL_CLOCK_V5_MISC_HDMI_30BPP              0x04
+#define PIXEL_CLOCK_V5_MISC_HDMI_32BPP              0x08
+#define PIXEL_CLOCK_V5_MISC_REF_DIV_SRC             0x10
+
+typedef struct _GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V2
+{
+  PIXEL_CLOCK_PARAMETERS_V3 sDispClkInput;
+}GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V2;
+
+typedef struct _GET_DISP_PLL_STATUS_OUTPUT_PARAMETERS_V2
+{
+  UCHAR  ucStatus;
+  UCHAR  ucRefDivSrc;                 // =1: reference clock source from XTALIN, =0: source from PCIE ref clock
+  UCHAR  ucReserved[2];
+}GET_DISP_PLL_STATUS_OUTPUT_PARAMETERS_V2;
+
+typedef struct _GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V3
+{
+  PIXEL_CLOCK_PARAMETERS_V5 sDispClkInput;
+}GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V3;
+
+/****************************************************************************/ 
+// Structures used by AdjustDisplayPllTable
+/****************************************************************************/ 
+typedef struct _ADJUST_DISPLAY_PLL_PARAMETERS
+{
        USHORT usPixelClock;
        UCHAR ucTransmitterID;
        UCHAR ucEncodeMode;
-       union {
-               UCHAR ucDVOConfig;      /* if DVO, need passing link rate and output 12bitlow or 24bit */
-               UCHAR ucConfig; /* if none DVO, not defined yet */
+       union
+       {
+               UCHAR ucDVOConfig;                                                                      //if DVO, need passing link rate and output 12bitlow or 24bit
+               UCHAR ucConfig;                                                                                 //if none DVO, not defined yet
        };
        UCHAR ucReserved[3];
-} ADJUST_DISPLAY_PLL_PARAMETERS;
+}ADJUST_DISPLAY_PLL_PARAMETERS;
 
 #define ADJUST_DISPLAY_CONFIG_SS_ENABLE       0x10
-
 #define ADJUST_DISPLAY_PLL_PS_ALLOCATION                       ADJUST_DISPLAY_PLL_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by EnableYUVTable */
-/****************************************************************************/
-typedef struct _ENABLE_YUV_PARAMETERS {
-       UCHAR ucEnable;         /*  ATOM_ENABLE:Enable YUV or ATOM_DISABLE:Disable YUV (RGB) */
-       UCHAR ucCRTC;           /*  Which CRTC needs this YUV or RGB format */
-       UCHAR ucPadding[2];
-} ENABLE_YUV_PARAMETERS;
+typedef struct _ADJUST_DISPLAY_PLL_INPUT_PARAMETERS_V3
+{
+       USHORT usPixelClock;                    // target pixel clock
+       UCHAR ucTransmitterID;                  // transmitter id defined in objectid.h
+       UCHAR ucEncodeMode;                     // encoder mode: CRT, LVDS, DP, TMDS or HDMI
+  UCHAR ucDispPllConfig;                 // display pll configure parameter defined as following DISPPLL_CONFIG_XXXX
+       UCHAR ucReserved[3];
+}ADJUST_DISPLAY_PLL_INPUT_PARAMETERS_V3;
+
+// usDispPllConfig v1.2 for RoadRunner
+#define DISPPLL_CONFIG_DVO_RATE_SEL                0x0001     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_DVO_DDR_SPEED               0x0000     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_DVO_SDR_SPEED               0x0001     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_DVO_OUTPUT_SEL              0x000c     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_DVO_LOW12BIT                0x0000     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_DVO_UPPER12BIT              0x0004     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_DVO_24BIT                   0x0008     // need only when ucTransmitterID = DVO
+#define DISPPLL_CONFIG_SS_ENABLE                   0x0010     // Only used when ucEncoderMode = DP or LVDS
+#define DISPPLL_CONFIG_COHERENT_MODE               0x0020     // Only used when ucEncoderMode = TMDS or HDMI
+#define DISPPLL_CONFIG_DUAL_LINK                   0x0040     // Only used when ucEncoderMode = TMDS or LVDS
+
+
+typedef struct _ADJUST_DISPLAY_PLL_OUTPUT_PARAMETERS_V3
+{
+  ULONG ulDispPllFreq;                 // return display PPLL freq which is used to generate the pixclock, and related idclk, symclk etc
+  UCHAR ucRefDiv;                      // if it is none-zero, it is used to be calculated the other ppll parameter fb_divider and post_div ( if it is not given )
+  UCHAR ucPostDiv;                     // if it is none-zero, it is used to be calculated the other ppll parameter fb_divider
+  UCHAR ucReserved[2];  
+}ADJUST_DISPLAY_PLL_OUTPUT_PARAMETERS_V3;
+
+typedef struct _ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3
+{
+  union 
+  {
+    ADJUST_DISPLAY_PLL_INPUT_PARAMETERS_V3  sInput;
+    ADJUST_DISPLAY_PLL_OUTPUT_PARAMETERS_V3 sOutput;
+  };
+} ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3;
+
+/****************************************************************************/ 
+// Structures used by EnableYUVTable
+/****************************************************************************/ 
+typedef struct _ENABLE_YUV_PARAMETERS
+{
+  UCHAR ucEnable;                     // ATOM_ENABLE:Enable YUV or ATOM_DISABLE:Disable YUV (RGB)
+  UCHAR ucCRTC;                       // Which CRTC needs this YUV or RGB format
+  UCHAR ucPadding[2];
+}ENABLE_YUV_PARAMETERS;
 #define ENABLE_YUV_PS_ALLOCATION ENABLE_YUV_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by GetMemoryClockTable */
-/****************************************************************************/
-typedef struct _GET_MEMORY_CLOCK_PARAMETERS {
-       ULONG ulReturnMemoryClock;      /*  current memory speed in 10KHz unit */
+/****************************************************************************/ 
+// Structures used by GetMemoryClockTable
+/****************************************************************************/ 
+typedef struct _GET_MEMORY_CLOCK_PARAMETERS
+{
+  ULONG ulReturnMemoryClock;          // current memory speed in 10KHz unit
 } GET_MEMORY_CLOCK_PARAMETERS;
 #define GET_MEMORY_CLOCK_PS_ALLOCATION  GET_MEMORY_CLOCK_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by GetEngineClockTable */
-/****************************************************************************/
-typedef struct _GET_ENGINE_CLOCK_PARAMETERS {
-       ULONG ulReturnEngineClock;      /*  current engine speed in 10KHz unit */
+/****************************************************************************/ 
+// Structures used by GetEngineClockTable
+/****************************************************************************/ 
+typedef struct _GET_ENGINE_CLOCK_PARAMETERS
+{
+  ULONG ulReturnEngineClock;          // current engine speed in 10KHz unit
 } GET_ENGINE_CLOCK_PARAMETERS;
 #define GET_ENGINE_CLOCK_PS_ALLOCATION  GET_ENGINE_CLOCK_PARAMETERS
 
-/****************************************************************************/
-/*  Following Structures and constant may be obsolete */
-/****************************************************************************/
-/* Maxium 8 bytes,the data read in will be placed in the parameter space. */
-/* Read operaion successeful when the paramter space is non-zero, otherwise read operation failed */
-typedef struct _READ_EDID_FROM_HW_I2C_DATA_PARAMETERS {
-       USHORT usPrescale;      /* Ratio between Engine clock and I2C clock */
-       USHORT usVRAMAddress;   /* Adress in Frame Buffer where to pace raw EDID */
-       USHORT usStatus;        /* When use output: lower byte EDID checksum, high byte hardware status */
-       /* WHen use input:  lower byte as 'byte to read':currently limited to 128byte or 1byte */
-       UCHAR ucSlaveAddr;      /* Read from which slave */
-       UCHAR ucLineNumber;     /* Read from which HW assisted line */
-} READ_EDID_FROM_HW_I2C_DATA_PARAMETERS;
+/****************************************************************************/ 
+// Following Structures and constant may be obsolete
+/****************************************************************************/ 
+//Maxium 8 bytes,the data read in will be placed in the parameter space.
+//Read operaion successeful when the paramter space is non-zero, otherwise read operation failed
+typedef struct _READ_EDID_FROM_HW_I2C_DATA_PARAMETERS
+{
+  USHORT    usPrescale;         //Ratio between Engine clock and I2C clock
+  USHORT    usVRAMAddress;      //Adress in Frame Buffer where to pace raw EDID
+  USHORT    usStatus;           //When use output: lower byte EDID checksum, high byte hardware status
+                                //WHen use input:  lower byte as 'byte to read':currently limited to 128byte or 1byte
+  UCHAR     ucSlaveAddr;        //Read from which slave
+  UCHAR     ucLineNumber;       //Read from which HW assisted line
+}READ_EDID_FROM_HW_I2C_DATA_PARAMETERS;
 #define READ_EDID_FROM_HW_I2C_DATA_PS_ALLOCATION  READ_EDID_FROM_HW_I2C_DATA_PARAMETERS
 
+
 #define  ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSDATABYTE                  0
 #define  ATOM_WRITE_I2C_FORMAT_PSOFFSET_PSTWODATABYTES              1
 #define  ATOM_WRITE_I2C_FORMAT_PSCOUNTER_PSOFFSET_IDDATABLOCK       2
 #define  ATOM_WRITE_I2C_FORMAT_PSCOUNTER_IDOFFSET_PLUS_IDDATABLOCK  3
 #define  ATOM_WRITE_I2C_FORMAT_IDCOUNTER_IDOFFSET_IDDATABLOCK       4
 
-typedef struct _WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS {
-       USHORT usPrescale;      /* Ratio between Engine clock and I2C clock */
-       USHORT usByteOffset;    /* Write to which byte */
-       /* Upper portion of usByteOffset is Format of data */
-       /* 1bytePS+offsetPS */
-       /* 2bytesPS+offsetPS */
-       /* blockID+offsetPS */
-       /* blockID+offsetID */
-       /* blockID+counterID+offsetID */
-       UCHAR ucData;           /* PS data1 */
-       UCHAR ucStatus;         /* Status byte 1=success, 2=failure, Also is used as PS data2 */
-       UCHAR ucSlaveAddr;      /* Write to which slave */
-       UCHAR ucLineNumber;     /* Write from which HW assisted line */
-} WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS;
+typedef struct _WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS
+{
+  USHORT    usPrescale;         //Ratio between Engine clock and I2C clock
+  USHORT    usByteOffset;       //Write to which byte
+                                //Upper portion of usByteOffset is Format of data 
+                                //1bytePS+offsetPS
+                                //2bytesPS+offsetPS
+                                //blockID+offsetPS
+                                //blockID+offsetID
+                                //blockID+counterID+offsetID
+  UCHAR     ucData;             //PS data1
+  UCHAR     ucStatus;           //Status byte 1=success, 2=failure, Also is used as PS data2
+  UCHAR     ucSlaveAddr;        //Write to which slave
+  UCHAR     ucLineNumber;       //Write from which HW assisted line
+}WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS;
 
 #define WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION  WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS
 
-typedef struct _SET_UP_HW_I2C_DATA_PARAMETERS {
-       USHORT usPrescale;      /* Ratio between Engine clock and I2C clock */
-       UCHAR ucSlaveAddr;      /* Write to which slave */
-       UCHAR ucLineNumber;     /* Write from which HW assisted line */
-} SET_UP_HW_I2C_DATA_PARAMETERS;
+typedef struct _SET_UP_HW_I2C_DATA_PARAMETERS
+{
+  USHORT    usPrescale;         //Ratio between Engine clock and I2C clock
+  UCHAR     ucSlaveAddr;        //Write to which slave
+  UCHAR     ucLineNumber;       //Write from which HW assisted line
+}SET_UP_HW_I2C_DATA_PARAMETERS;
+
 
 /**************************************************************************/
 #define SPEED_FAN_CONTROL_PS_ALLOCATION   WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS
 
-/****************************************************************************/
-/*  Structures used by PowerConnectorDetectionTable */
-/****************************************************************************/
-typedef struct _POWER_CONNECTOR_DETECTION_PARAMETERS {
-       UCHAR ucPowerConnectorStatus;   /* Used for return value 0: detected, 1:not detected */
-       UCHAR ucPwrBehaviorId;
-       USHORT usPwrBudget;     /* how much power currently boot to in unit of watt */
-} POWER_CONNECTOR_DETECTION_PARAMETERS;
-
-typedef struct POWER_CONNECTOR_DETECTION_PS_ALLOCATION {
-       UCHAR ucPowerConnectorStatus;   /* Used for return value 0: detected, 1:not detected */
-       UCHAR ucReserved;
-       USHORT usPwrBudget;     /* how much power currently boot to in unit of watt */
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;
-} POWER_CONNECTOR_DETECTION_PS_ALLOCATION;
+/****************************************************************************/ 
+// Structures used by PowerConnectorDetectionTable
+/****************************************************************************/ 
+typedef struct _POWER_CONNECTOR_DETECTION_PARAMETERS
+{
+  UCHAR   ucPowerConnectorStatus;      //Used for return value 0: detected, 1:not detected
+       UCHAR   ucPwrBehaviorId;                                                        
+       USHORT  usPwrBudget;                                                             //how much power currently boot to in unit of watt
+}POWER_CONNECTOR_DETECTION_PARAMETERS;
+
+typedef struct POWER_CONNECTOR_DETECTION_PS_ALLOCATION
+{                               
+  UCHAR   ucPowerConnectorStatus;      //Used for return value 0: detected, 1:not detected
+       UCHAR   ucReserved;
+       USHORT  usPwrBudget;                                                             //how much power currently boot to in unit of watt
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION    sReserved;
+}POWER_CONNECTOR_DETECTION_PS_ALLOCATION;
 
 /****************************LVDS SS Command Table Definitions**********************/
 
-/****************************************************************************/
-/*  Structures used by EnableSpreadSpectrumOnPPLLTable */
-/****************************************************************************/
-typedef struct _ENABLE_LVDS_SS_PARAMETERS {
-       USHORT usSpreadSpectrumPercentage;
-       UCHAR ucSpreadSpectrumType;     /* Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD */
-       UCHAR ucSpreadSpectrumStepSize_Delay;   /* bits3:2 SS_STEP_SIZE; bit 6:4 SS_DELAY */
-       UCHAR ucEnable;         /* ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucPadding[3];
-} ENABLE_LVDS_SS_PARAMETERS;
-
-/* ucTableFormatRevision=1,ucTableContentRevision=2 */
-typedef struct _ENABLE_LVDS_SS_PARAMETERS_V2 {
-       USHORT usSpreadSpectrumPercentage;
-       UCHAR ucSpreadSpectrumType;     /* Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD */
-       UCHAR ucSpreadSpectrumStep;     /*  */
-       UCHAR ucEnable;         /* ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucSpreadSpectrumDelay;
-       UCHAR ucSpreadSpectrumRange;
-       UCHAR ucPadding;
-} ENABLE_LVDS_SS_PARAMETERS_V2;
-
-/* This new structure is based on ENABLE_LVDS_SS_PARAMETERS but expands to SS on PPLL, so other devices can use SS. */
-typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL {
-       USHORT usSpreadSpectrumPercentage;
-       UCHAR ucSpreadSpectrumType;     /*  Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD */
-       UCHAR ucSpreadSpectrumStep;     /*  */
-       UCHAR ucEnable;         /*  ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucSpreadSpectrumDelay;
-       UCHAR ucSpreadSpectrumRange;
-       UCHAR ucPpll;           /*  ATOM_PPLL1/ATOM_PPLL2 */
-} ENABLE_SPREAD_SPECTRUM_ON_PPLL;
+/****************************************************************************/ 
+// Structures used by EnableSpreadSpectrumOnPPLLTable
+/****************************************************************************/ 
+typedef struct _ENABLE_LVDS_SS_PARAMETERS
+{
+  USHORT  usSpreadSpectrumPercentage;       
+  UCHAR   ucSpreadSpectrumType;           //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD
+  UCHAR   ucSpreadSpectrumStepSize_Delay; //bits3:2 SS_STEP_SIZE; bit 6:4 SS_DELAY
+  UCHAR   ucEnable;                       //ATOM_ENABLE or ATOM_DISABLE
+  UCHAR   ucPadding[3];
+}ENABLE_LVDS_SS_PARAMETERS;
+
+//ucTableFormatRevision=1,ucTableContentRevision=2
+typedef struct _ENABLE_LVDS_SS_PARAMETERS_V2
+{
+  USHORT  usSpreadSpectrumPercentage;       
+  UCHAR   ucSpreadSpectrumType;           //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD
+  UCHAR   ucSpreadSpectrumStep;           //
+  UCHAR   ucEnable;                       //ATOM_ENABLE or ATOM_DISABLE
+  UCHAR   ucSpreadSpectrumDelay;
+  UCHAR   ucSpreadSpectrumRange;
+  UCHAR   ucPadding;
+}ENABLE_LVDS_SS_PARAMETERS_V2;
+
+//This new structure is based on ENABLE_LVDS_SS_PARAMETERS but expands to SS on PPLL, so other devices can use SS.
+typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL
+{
+  USHORT  usSpreadSpectrumPercentage;
+  UCHAR   ucSpreadSpectrumType;           // Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD
+  UCHAR   ucSpreadSpectrumStep;           //
+  UCHAR   ucEnable;                       // ATOM_ENABLE or ATOM_DISABLE
+  UCHAR   ucSpreadSpectrumDelay;
+  UCHAR   ucSpreadSpectrumRange;
+  UCHAR   ucPpll;                                                                                                // ATOM_PPLL1/ATOM_PPLL2
+}ENABLE_SPREAD_SPECTRUM_ON_PPLL;
+
+typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2
+{
+  USHORT  usSpreadSpectrumPercentage;
+  UCHAR   ucSpreadSpectrumType;                // Bit[0]: 0-Down Spread,1-Center Spread. 
+                                        // Bit[1]: 1-Ext. 0-Int. 
+                                        // Bit[3:2]: =0 P1PLL =1 P2PLL =2 DCPLL
+                                        // Bits[7:4] reserved
+  UCHAR   ucEnable;                        // ATOM_ENABLE or ATOM_DISABLE
+  USHORT  usSpreadSpectrumAmount;              // Includes SS_AMOUNT_FBDIV[7:0] and SS_AMOUNT_NFRAC_SLIP[11:8]    
+  USHORT  usSpreadSpectrumStep;                // SS_STEP_SIZE_DSFRAC
+}ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2;
+
+#define ATOM_PPLL_SS_TYPE_V2_DOWN_SPREAD      0x00
+#define ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD    0x01
+#define ATOM_PPLL_SS_TYPE_V2_EXT_SPREAD       0x02
+#define ATOM_PPLL_SS_TYPE_V2_PPLL_SEL_MASK    0x0c
+#define ATOM_PPLL_SS_TYPE_V2_P1PLL            0x00
+#define ATOM_PPLL_SS_TYPE_V2_P2PLL            0x04
+#define ATOM_PPLL_SS_TYPE_V2_DCPLL            0x08
+#define ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK     0x00FF
+#define ATOM_PPLL_SS_AMOUNT_V2_FBDIV_SHIFT    0
+#define ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK     0x0F00
+#define ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT    8
 
 #define ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION  ENABLE_SPREAD_SPECTRUM_ON_PPLL
 
 /**************************************************************************/
 
-typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION {
-       PIXEL_CLOCK_PARAMETERS sPCLKInput;
-       ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved;       /* Caller doesn't need to init this portion */
-} SET_PIXEL_CLOCK_PS_ALLOCATION;
+typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION
+{
+  PIXEL_CLOCK_PARAMETERS sPCLKInput;
+  ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved;//Caller doesn't need to init this portion 
+}SET_PIXEL_CLOCK_PS_ALLOCATION;
 
 #define ENABLE_VGA_RENDER_PS_ALLOCATION   SET_PIXEL_CLOCK_PS_ALLOCATION
 
-/****************************************************************************/
-/*  Structures used by ### */
-/****************************************************************************/
-typedef struct _MEMORY_TRAINING_PARAMETERS {
-       ULONG ulTargetMemoryClock;      /* In 10Khz unit */
-} MEMORY_TRAINING_PARAMETERS;
+/****************************************************************************/ 
+// Structures used by ###
+/****************************************************************************/ 
+typedef struct _MEMORY_TRAINING_PARAMETERS
+{
+  ULONG ulTargetMemoryClock;          //In 10Khz unit
+}MEMORY_TRAINING_PARAMETERS;
 #define MEMORY_TRAINING_PS_ALLOCATION MEMORY_TRAINING_PARAMETERS
 
+
 /****************************LVDS and other encoder command table definitions **********************/
 
-/****************************************************************************/
-/*  Structures used by LVDSEncoderControlTable   (Before DCE30) */
-/*                     LVTMAEncoderControlTable  (Before DCE30) */
-/*                     TMDSAEncoderControlTable  (Before DCE30) */
-/****************************************************************************/
-typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS {
-       USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       UCHAR ucMisc;           /*  bit0=0: Enable single link */
-       /*      =1: Enable dual link */
-       /*  Bit1=0: 666RGB */
-       /*      =1: 888RGB */
-       UCHAR ucAction;         /*  0: turn off encoder */
-       /*  1: setup and turn on encoder */
-} LVDS_ENCODER_CONTROL_PARAMETERS;
 
-#define LVDS_ENCODER_CONTROL_PS_ALLOCATION  LVDS_ENCODER_CONTROL_PARAMETERS
+/****************************************************************************/ 
+// Structures used by LVDSEncoderControlTable   (Before DCE30)
+//                    LVTMAEncoderControlTable  (Before DCE30)
+//                    TMDSAEncoderControlTable  (Before DCE30)
+/****************************************************************************/ 
+typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS
+{
+  USHORT usPixelClock;  // in 10KHz; for bios convenient
+  UCHAR  ucMisc;        // bit0=0: Enable single link
+                        //     =1: Enable dual link
+                        // Bit1=0: 666RGB
+                        //     =1: 888RGB
+  UCHAR  ucAction;      // 0: turn off encoder
+                        // 1: setup and turn on encoder
+}LVDS_ENCODER_CONTROL_PARAMETERS;
 
+#define LVDS_ENCODER_CONTROL_PS_ALLOCATION  LVDS_ENCODER_CONTROL_PARAMETERS
+   
 #define TMDS1_ENCODER_CONTROL_PARAMETERS    LVDS_ENCODER_CONTROL_PARAMETERS
 #define TMDS1_ENCODER_CONTROL_PS_ALLOCATION TMDS1_ENCODER_CONTROL_PARAMETERS
 
 #define TMDS2_ENCODER_CONTROL_PARAMETERS    TMDS1_ENCODER_CONTROL_PARAMETERS
 #define TMDS2_ENCODER_CONTROL_PS_ALLOCATION TMDS2_ENCODER_CONTROL_PARAMETERS
 
-/* ucTableFormatRevision=1,ucTableContentRevision=2 */
-typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 {
-       USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       UCHAR ucMisc;           /*  see PANEL_ENCODER_MISC_xx definitions below */
-       UCHAR ucAction;         /*  0: turn off encoder */
-       /*  1: setup and turn on encoder */
-       UCHAR ucTruncate;       /*  bit0=0: Disable truncate */
-       /*      =1: Enable truncate */
-       /*  bit4=0: 666RGB */
-       /*      =1: 888RGB */
-       UCHAR ucSpatial;        /*  bit0=0: Disable spatial dithering */
-       /*      =1: Enable spatial dithering */
-       /*  bit4=0: 666RGB */
-       /*      =1: 888RGB */
-       UCHAR ucTemporal;       /*  bit0=0: Disable temporal dithering */
-       /*      =1: Enable temporal dithering */
-       /*  bit4=0: 666RGB */
-       /*      =1: 888RGB */
-       /*  bit5=0: Gray level 2 */
-       /*      =1: Gray level 4 */
-       UCHAR ucFRC;            /*  bit4=0: 25FRC_SEL pattern E */
-       /*      =1: 25FRC_SEL pattern F */
-       /*  bit6:5=0: 50FRC_SEL pattern A */
-       /*        =1: 50FRC_SEL pattern B */
-       /*        =2: 50FRC_SEL pattern C */
-       /*        =3: 50FRC_SEL pattern D */
-       /*  bit7=0: 75FRC_SEL pattern E */
-       /*      =1: 75FRC_SEL pattern F */
-} LVDS_ENCODER_CONTROL_PARAMETERS_V2;
 
-#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2  LVDS_ENCODER_CONTROL_PARAMETERS_V2
+//ucTableFormatRevision=1,ucTableContentRevision=2
+typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2
+{
+  USHORT usPixelClock;  // in 10KHz; for bios convenient
+  UCHAR  ucMisc;        // see PANEL_ENCODER_MISC_xx defintions below
+  UCHAR  ucAction;      // 0: turn off encoder
+                        // 1: setup and turn on encoder
+  UCHAR  ucTruncate;    // bit0=0: Disable truncate
+                        //     =1: Enable truncate
+                        // bit4=0: 666RGB
+                        //     =1: 888RGB
+  UCHAR  ucSpatial;     // bit0=0: Disable spatial dithering
+                        //     =1: Enable spatial dithering
+                        // bit4=0: 666RGB
+                        //     =1: 888RGB
+  UCHAR  ucTemporal;    // bit0=0: Disable temporal dithering
+                        //     =1: Enable temporal dithering
+                        // bit4=0: 666RGB
+                        //     =1: 888RGB
+                        // bit5=0: Gray level 2
+                        //     =1: Gray level 4
+  UCHAR  ucFRC;         // bit4=0: 25FRC_SEL pattern E
+                        //     =1: 25FRC_SEL pattern F
+                        // bit6:5=0: 50FRC_SEL pattern A
+                        //       =1: 50FRC_SEL pattern B
+                        //       =2: 50FRC_SEL pattern C
+                        //       =3: 50FRC_SEL pattern D
+                        // bit7=0: 75FRC_SEL pattern E
+                        //     =1: 75FRC_SEL pattern F
+}LVDS_ENCODER_CONTROL_PARAMETERS_V2;
 
+#define LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2  LVDS_ENCODER_CONTROL_PARAMETERS_V2
+   
 #define TMDS1_ENCODER_CONTROL_PARAMETERS_V2    LVDS_ENCODER_CONTROL_PARAMETERS_V2
 #define TMDS1_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS1_ENCODER_CONTROL_PARAMETERS_V2
-
+  
 #define TMDS2_ENCODER_CONTROL_PARAMETERS_V2    TMDS1_ENCODER_CONTROL_PARAMETERS_V2
 #define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V2 TMDS2_ENCODER_CONTROL_PARAMETERS_V2
 
@@ -1185,38 +1536,42 @@ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 {
 #define TMDS2_ENCODER_CONTROL_PARAMETERS_V3    LVDS_ENCODER_CONTROL_PARAMETERS_V3
 #define TMDS2_ENCODER_CONTROL_PS_ALLOCATION_V3 TMDS2_ENCODER_CONTROL_PARAMETERS_V3
 
-/****************************************************************************/
-/*  Structures used by ### */
-/****************************************************************************/
-typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS {
-       UCHAR ucEnable;         /*  Enable or Disable External TMDS encoder */
-       UCHAR ucMisc;           /*  Bit0=0:Enable Single link;=1:Enable Dual link;Bit1 {=0:666RGB, =1:888RGB} */
-       UCHAR ucPadding[2];
-} ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS;
-
-typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION {
-       ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS sXTmdsEncoder;
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;     /* Caller doesn't need to init this portion */
-} ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION;
+/****************************************************************************/ 
+// Structures used by ###
+/****************************************************************************/ 
+typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS
+{                               
+  UCHAR    ucEnable;            // Enable or Disable External TMDS encoder
+  UCHAR    ucMisc;              // Bit0=0:Enable Single link;=1:Enable Dual link;Bit1 {=0:666RGB, =1:888RGB}
+  UCHAR    ucPadding[2];
+}ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS;
+
+typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION
+{                               
+  ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS    sXTmdsEncoder;
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION   sReserved;     //Caller doesn't need to init this portion
+}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION;
 
 #define ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2  LVDS_ENCODER_CONTROL_PARAMETERS_V2
 
-typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2 {
-       ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2 sXTmdsEncoder;
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;     /* Caller doesn't need to init this portion */
-} ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2;
+typedef struct _ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2
+{                               
+  ENABLE_EXTERNAL_TMDS_ENCODER_PARAMETERS_V2    sXTmdsEncoder;
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION      sReserved;     //Caller doesn't need to init this portion
+}ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION_V2;
 
-typedef struct _EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION {
-       DIG_ENCODER_CONTROL_PARAMETERS sDigEncoder;
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;
-} EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION;
+typedef struct _EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION
+{
+  DIG_ENCODER_CONTROL_PARAMETERS            sDigEncoder;
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;
+}EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION;
 
-/****************************************************************************/
-/*  Structures used by DVOEncoderControlTable */
-/****************************************************************************/
-/* ucTableFormatRevision=1,ucTableContentRevision=3 */
+/****************************************************************************/ 
+// Structures used by DVOEncoderControlTable
+/****************************************************************************/ 
+//ucTableFormatRevision=1,ucTableContentRevision=3
 
-/* ucDVOConfig: */
+//ucDVOConfig:
 #define DVO_ENCODER_CONFIG_RATE_SEL                                                    0x01
 #define DVO_ENCODER_CONFIG_DDR_SPEED                                           0x00
 #define DVO_ENCODER_CONFIG_SDR_SPEED                                           0x01
@@ -1225,21 +1580,22 @@ typedef struct _EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION {
 #define DVO_ENCODER_CONFIG_UPPER12BIT                                          0x04
 #define DVO_ENCODER_CONFIG_24BIT                                                               0x08
 
-typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 {
-       USHORT usPixelClock;
-       UCHAR ucDVOConfig;
-       UCHAR ucAction;         /* ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT */
-       UCHAR ucReseved[4];
-} DVO_ENCODER_CONTROL_PARAMETERS_V3;
+typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3
+{
+  USHORT usPixelClock; 
+  UCHAR  ucDVOConfig;
+  UCHAR  ucAction;                                                                                                             //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT
+  UCHAR  ucReseved[4];
+}DVO_ENCODER_CONTROL_PARAMETERS_V3;
 #define DVO_ENCODER_CONTROL_PS_ALLOCATION_V3   DVO_ENCODER_CONTROL_PARAMETERS_V3
 
-/* ucTableFormatRevision=1 */
-/* ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for */
-/*  bit1=0: non-coherent mode */
-/*      =1: coherent mode */
+//ucTableFormatRevision=1
+//ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for 
+// bit1=0: non-coherent mode
+//     =1: coherent mode
 
-/* ========================================================================================== */
-/* Only change is here next time when changing encoder parameter definitions again! */
+//==========================================================================================
+//Only change is here next time when changing encoder parameter definitions again!
 #define LVDS_ENCODER_CONTROL_PARAMETERS_LAST     LVDS_ENCODER_CONTROL_PARAMETERS_V3
 #define LVDS_ENCODER_CONTROL_PS_ALLOCATION_LAST  LVDS_ENCODER_CONTROL_PARAMETERS_LAST
 
@@ -1252,7 +1608,7 @@ typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 {
 #define DVO_ENCODER_CONTROL_PARAMETERS_LAST      DVO_ENCODER_CONTROL_PARAMETERS
 #define DVO_ENCODER_CONTROL_PS_ALLOCATION_LAST   DVO_ENCODER_CONTROL_PS_ALLOCATION
 
-/* ========================================================================================== */
+//==========================================================================================
 #define PANEL_ENCODER_MISC_DUAL                0x01
 #define PANEL_ENCODER_MISC_COHERENT            0x02
 #define        PANEL_ENCODER_MISC_TMDS_LINKB                                    0x04
@@ -1281,159 +1637,159 @@ typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 {
 #define PANEL_ENCODER_75FRC_E                  0x00
 #define PANEL_ENCODER_75FRC_F                  0x80
 
-/****************************************************************************/
-/*  Structures used by SetVoltageTable */
-/****************************************************************************/
+/****************************************************************************/ 
+// Structures used by SetVoltageTable
+/****************************************************************************/ 
 #define SET_VOLTAGE_TYPE_ASIC_VDDC             1
 #define SET_VOLTAGE_TYPE_ASIC_MVDDC            2
 #define SET_VOLTAGE_TYPE_ASIC_MVDDQ            3
 #define SET_VOLTAGE_TYPE_ASIC_VDDCI            4
 #define SET_VOLTAGE_INIT_MODE                  5
-#define SET_VOLTAGE_GET_MAX_VOLTAGE            6       /* Gets the Max. voltage for the soldered Asic */
+#define SET_VOLTAGE_GET_MAX_VOLTAGE            6                                       //Gets the Max. voltage for the soldered Asic
 
 #define SET_ASIC_VOLTAGE_MODE_ALL_SOURCE       0x1
 #define SET_ASIC_VOLTAGE_MODE_SOURCE_A         0x2
 #define SET_ASIC_VOLTAGE_MODE_SOURCE_B         0x4
 
 #define        SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE      0x0
-#define        SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL      0x1
+#define        SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL      0x1      
 #define        SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK     0x2
 
-typedef struct _SET_VOLTAGE_PARAMETERS {
-       UCHAR ucVoltageType;    /*  To tell which voltage to set up, VDDC/MVDDC/MVDDQ */
-       UCHAR ucVoltageMode;    /*  To set all, to set source A or source B or ... */
-       UCHAR ucVoltageIndex;   /*  An index to tell which voltage level */
-       UCHAR ucReserved;
-} SET_VOLTAGE_PARAMETERS;
-
-typedef struct _SET_VOLTAGE_PARAMETERS_V2 {
-       UCHAR ucVoltageType;    /*  To tell which voltage to set up, VDDC/MVDDC/MVDDQ */
-       UCHAR ucVoltageMode;    /*  Not used, maybe use for state machine for differen power mode */
-       USHORT usVoltageLevel;  /*  real voltage level */
-} SET_VOLTAGE_PARAMETERS_V2;
-
-typedef struct _SET_VOLTAGE_PS_ALLOCATION {
-       SET_VOLTAGE_PARAMETERS sASICSetVoltage;
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;
-} SET_VOLTAGE_PS_ALLOCATION;
-
-/****************************************************************************/
-/*  Structures used by TVEncoderControlTable */
-/****************************************************************************/
-typedef struct _TV_ENCODER_CONTROL_PARAMETERS {
-       USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       UCHAR ucTvStandard;     /*  See definition "ATOM_TV_NTSC ..." */
-       UCHAR ucAction;         /*  0: turn off encoder */
-       /*  1: setup and turn on encoder */
-} TV_ENCODER_CONTROL_PARAMETERS;
-
-typedef struct _TV_ENCODER_CONTROL_PS_ALLOCATION {
-       TV_ENCODER_CONTROL_PARAMETERS sTVEncoder;
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;     /*  Don't set this one */
-} TV_ENCODER_CONTROL_PS_ALLOCATION;
-
-/* ==============================Data Table Portion==================================== */
-
-#ifdef UEFI_BUILD
-#define        UTEMP   USHORT
-#define        USHORT  void*
-#endif
-
-/****************************************************************************/
-/*  Structure used in Data.mtb */
-/****************************************************************************/
-typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES {
-       USHORT UtilityPipeLine; /*  Offest for the utility to get parser info,Don't change this position! */
-       USHORT MultimediaCapabilityInfo;        /*  Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios */
-       USHORT MultimediaConfigInfo;    /*  Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios */
-       USHORT StandardVESA_Timing;     /*  Only used by Bios */
-       USHORT FirmwareInfo;    /*  Shared by various SW components,latest version 1.4 */
-       USHORT DAC_Info;        /*  Will be obsolete from R600 */
-       USHORT LVDS_Info;       /*  Shared by various SW components,latest version 1.1 */
-       USHORT TMDS_Info;       /*  Will be obsolete from R600 */
-       USHORT AnalogTV_Info;   /*  Shared by various SW components,latest version 1.1 */
-       USHORT SupportedDevicesInfo;    /*  Will be obsolete from R600 */
-       USHORT GPIO_I2C_Info;   /*  Shared by various SW components,latest version 1.2 will be used from R600 */
-       USHORT VRAM_UsageByFirmware;    /*  Shared by various SW components,latest version 1.3 will be used from R600 */
-       USHORT GPIO_Pin_LUT;    /*  Shared by various SW components,latest version 1.1 */
-       USHORT VESA_ToInternalModeLUT;  /*  Only used by Bios */
-       USHORT ComponentVideoInfo;      /*  Shared by various SW components,latest version 2.1 will be used from R600 */
-       USHORT PowerPlayInfo;   /*  Shared by various SW components,latest version 2.1,new design from R600 */
-       USHORT CompassionateData;       /*  Will be obsolete from R600 */
-       USHORT SaveRestoreInfo; /*  Only used by Bios */
-       USHORT PPLL_SS_Info;    /*  Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info */
-       USHORT OemInfo;         /*  Defined and used by external SW, should be obsolete soon */
-       USHORT XTMDS_Info;      /*  Will be obsolete from R600 */
-       USHORT MclkSS_Info;     /*  Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used */
-       USHORT Object_Header;   /*  Shared by various SW components,latest version 1.1 */
-       USHORT IndirectIOAccess;        /*  Only used by Bios,this table position can't change at all!! */
-       USHORT MC_InitParameter;        /*  Only used by command table */
-       USHORT ASIC_VDDC_Info;  /*  Will be obsolete from R600 */
-       USHORT ASIC_InternalSS_Info;    /*  New tabel name from R600, used to be called "ASIC_MVDDC_Info" */
-       USHORT TV_VideoMode;    /*  Only used by command table */
-       USHORT VRAM_Info;       /*  Only used by command table, latest version 1.3 */
-       USHORT MemoryTrainingInfo;      /*  Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1 */
-       USHORT IntegratedSystemInfo;    /*  Shared by various SW components */
-       USHORT ASIC_ProfilingInfo;      /*  New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600 */
-       USHORT VoltageObjectInfo;       /*  Shared by various SW components, latest version 1.1 */
-       USHORT PowerSourceInfo; /*  Shared by various SW components, latest versoin 1.1 */
-} ATOM_MASTER_LIST_OF_DATA_TABLES;
+typedef struct _SET_VOLTAGE_PARAMETERS
+{
+  UCHAR    ucVoltageType;               // To tell which voltage to set up, VDDC/MVDDC/MVDDQ
+  UCHAR    ucVoltageMode;               // To set all, to set source A or source B or ...
+  UCHAR    ucVoltageIndex;              // An index to tell which voltage level
+  UCHAR    ucReserved;          
+}SET_VOLTAGE_PARAMETERS;
 
-#ifdef UEFI_BUILD
-#define        USHORT  UTEMP
-#endif
+typedef struct _SET_VOLTAGE_PARAMETERS_V2
+{
+  UCHAR    ucVoltageType;               // To tell which voltage to set up, VDDC/MVDDC/MVDDQ
+  UCHAR    ucVoltageMode;               // Not used, maybe use for state machine for differen power mode
+  USHORT   usVoltageLevel;              // real voltage level
+}SET_VOLTAGE_PARAMETERS_V2;
 
-typedef struct _ATOM_MASTER_DATA_TABLE {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_MASTER_LIST_OF_DATA_TABLES ListOfDataTables;
-} ATOM_MASTER_DATA_TABLE;
+typedef struct _SET_VOLTAGE_PS_ALLOCATION
+{
+  SET_VOLTAGE_PARAMETERS sASICSetVoltage;
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;
+}SET_VOLTAGE_PS_ALLOCATION;
+
+/****************************************************************************/ 
+// Structures used by TVEncoderControlTable
+/****************************************************************************/ 
+typedef struct _TV_ENCODER_CONTROL_PARAMETERS
+{
+  USHORT usPixelClock;                // in 10KHz; for bios convenient
+  UCHAR  ucTvStandard;                // See definition "ATOM_TV_NTSC ..."
+  UCHAR  ucAction;                    // 0: turn off encoder
+                                      // 1: setup and turn on encoder
+}TV_ENCODER_CONTROL_PARAMETERS;
 
-/****************************************************************************/
-/*  Structure used in MultimediaCapabilityInfoTable */
-/****************************************************************************/
-typedef struct _ATOM_MULTIMEDIA_CAPABILITY_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulSignature;      /*  HW info table signature string "$ATI" */
-       UCHAR ucI2C_Type;       /*  I2C type (normal GP_IO, ImpactTV GP_IO, Dedicated I2C pin, etc) */
-       UCHAR ucTV_OutInfo;     /*  Type of TV out supported (3:0) and video out crystal frequency (6:4) and TV data port (7) */
-       UCHAR ucVideoPortInfo;  /*  Provides the video port capabilities */
-       UCHAR ucHostPortInfo;   /*  Provides host port configuration information */
-} ATOM_MULTIMEDIA_CAPABILITY_INFO;
+typedef struct _TV_ENCODER_CONTROL_PS_ALLOCATION
+{
+  TV_ENCODER_CONTROL_PARAMETERS sTVEncoder;          
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION    sReserved; // Don't set this one
+}TV_ENCODER_CONTROL_PS_ALLOCATION;
 
-/****************************************************************************/
-/*  Structure used in MultimediaConfigInfoTable */
-/****************************************************************************/
-typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulSignature;      /*  MM info table signature sting "$MMT" */
-       UCHAR ucTunerInfo;      /*  Type of tuner installed on the adapter (4:0) and video input for tuner (7:5) */
-       UCHAR ucAudioChipInfo;  /*  List the audio chip type (3:0) product type (4) and OEM revision (7:5) */
-       UCHAR ucProductID;      /*  Defines as OEM ID or ATI board ID dependent on product type setting */
-       UCHAR ucMiscInfo1;      /*  Tuner voltage (1:0) HW teletext support (3:2) FM audio decoder (5:4) reserved (6) audio scrambling (7) */
-       UCHAR ucMiscInfo2;      /*  I2S input config (0) I2S output config (1) I2S Audio Chip (4:2) SPDIF Output Config (5) reserved (7:6) */
-       UCHAR ucMiscInfo3;      /*  Video Decoder Type (3:0) Video In Standard/Crystal (7:4) */
-       UCHAR ucMiscInfo4;      /*  Video Decoder Host Config (2:0) reserved (7:3) */
-       UCHAR ucVideoInput0Info;        /*  Video Input 0 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) */
-       UCHAR ucVideoInput1Info;        /*  Video Input 1 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) */
-       UCHAR ucVideoInput2Info;        /*  Video Input 2 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) */
-       UCHAR ucVideoInput3Info;        /*  Video Input 3 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) */
-       UCHAR ucVideoInput4Info;        /*  Video Input 4 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6) */
-} ATOM_MULTIMEDIA_CONFIG_INFO;
+//==============================Data Table Portion====================================
 
-/****************************************************************************/
-/*  Structures used in FirmwareInfoTable */
-/****************************************************************************/
-
-/*  usBIOSCapability Definition: */
-/*  Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; */
-/*  Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; */
-/*  Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; */
-/*  Others: Reserved */
+/****************************************************************************/ 
+// Structure used in Data.mtb
+/****************************************************************************/ 
+typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES
+{
+  USHORT        UtilityPipeLine;               // Offest for the utility to get parser info,Don't change this position!
+  USHORT        MultimediaCapabilityInfo; // Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios 
+  USHORT        MultimediaConfigInfo;     // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios
+  USHORT        StandardVESA_Timing;      // Only used by Bios
+  USHORT        FirmwareInfo;             // Shared by various SW components,latest version 1.4
+  USHORT        DAC_Info;                 // Will be obsolete from R600
+  USHORT        LVDS_Info;                // Shared by various SW components,latest version 1.1 
+  USHORT        TMDS_Info;                // Will be obsolete from R600
+  USHORT        AnalogTV_Info;            // Shared by various SW components,latest version 1.1 
+  USHORT        SupportedDevicesInfo;     // Will be obsolete from R600
+  USHORT        GPIO_I2C_Info;            // Shared by various SW components,latest version 1.2 will be used from R600           
+  USHORT        VRAM_UsageByFirmware;     // Shared by various SW components,latest version 1.3 will be used from R600
+  USHORT        GPIO_Pin_LUT;             // Shared by various SW components,latest version 1.1
+  USHORT        VESA_ToInternalModeLUT;   // Only used by Bios
+  USHORT        ComponentVideoInfo;       // Shared by various SW components,latest version 2.1 will be used from R600
+  USHORT        PowerPlayInfo;            // Shared by various SW components,latest version 2.1,new design from R600
+  USHORT        CompassionateData;        // Will be obsolete from R600
+  USHORT        SaveRestoreInfo;          // Only used by Bios
+  USHORT        PPLL_SS_Info;             // Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info
+  USHORT        OemInfo;                  // Defined and used by external SW, should be obsolete soon
+  USHORT        XTMDS_Info;               // Will be obsolete from R600
+  USHORT        MclkSS_Info;              // Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used
+  USHORT        Object_Header;            // Shared by various SW components,latest version 1.1
+  USHORT        IndirectIOAccess;         // Only used by Bios,this table position can't change at all!!
+  USHORT        MC_InitParameter;         // Only used by command table
+  USHORT        ASIC_VDDC_Info;                                                // Will be obsolete from R600
+  USHORT        ASIC_InternalSS_Info;                  // New tabel name from R600, used to be called "ASIC_MVDDC_Info"
+  USHORT        TV_VideoMode;                                                  // Only used by command table
+  USHORT        VRAM_Info;                                                             // Only used by command table, latest version 1.3
+  USHORT        MemoryTrainingInfo;                            // Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1
+  USHORT        IntegratedSystemInfo;                  // Shared by various SW components
+  USHORT        ASIC_ProfilingInfo;                            // New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600
+  USHORT        VoltageObjectInfo;                             // Shared by various SW components, latest version 1.1
+       USHORT                          PowerSourceInfo;                                        // Shared by various SW components, latest versoin 1.1
+}ATOM_MASTER_LIST_OF_DATA_TABLES;
+
+typedef struct _ATOM_MASTER_DATA_TABLE
+{ 
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ATOM_MASTER_LIST_OF_DATA_TABLES   ListOfDataTables;
+}ATOM_MASTER_DATA_TABLE;
+
+/****************************************************************************/ 
+// Structure used in MultimediaCapabilityInfoTable
+/****************************************************************************/ 
+typedef struct _ATOM_MULTIMEDIA_CAPABILITY_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ULONG                    ulSignature;      // HW info table signature string "$ATI"
+  UCHAR                    ucI2C_Type;       // I2C type (normal GP_IO, ImpactTV GP_IO, Dedicated I2C pin, etc)
+  UCHAR                    ucTV_OutInfo;     // Type of TV out supported (3:0) and video out crystal frequency (6:4) and TV data port (7)
+  UCHAR                    ucVideoPortInfo;  // Provides the video port capabilities
+  UCHAR                    ucHostPortInfo;   // Provides host port configuration information
+}ATOM_MULTIMEDIA_CAPABILITY_INFO;
+
+/****************************************************************************/ 
+// Structure used in MultimediaConfigInfoTable
+/****************************************************************************/ 
+typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;
+  ULONG                    ulSignature;      // MM info table signature sting "$MMT"
+  UCHAR                    ucTunerInfo;      // Type of tuner installed on the adapter (4:0) and video input for tuner (7:5)
+  UCHAR                    ucAudioChipInfo;  // List the audio chip type (3:0) product type (4) and OEM revision (7:5)
+  UCHAR                    ucProductID;      // Defines as OEM ID or ATI board ID dependent on product type setting
+  UCHAR                    ucMiscInfo1;      // Tuner voltage (1:0) HW teletext support (3:2) FM audio decoder (5:4) reserved (6) audio scrambling (7)
+  UCHAR                    ucMiscInfo2;      // I2S input config (0) I2S output config (1) I2S Audio Chip (4:2) SPDIF Output Config (5) reserved (7:6)
+  UCHAR                    ucMiscInfo3;      // Video Decoder Type (3:0) Video In Standard/Crystal (7:4)
+  UCHAR                    ucMiscInfo4;      // Video Decoder Host Config (2:0) reserved (7:3)
+  UCHAR                    ucVideoInput0Info;// Video Input 0 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6)
+  UCHAR                    ucVideoInput1Info;// Video Input 1 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6)
+  UCHAR                    ucVideoInput2Info;// Video Input 2 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6)
+  UCHAR                    ucVideoInput3Info;// Video Input 3 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6)
+  UCHAR                    ucVideoInput4Info;// Video Input 4 Type (1:0) F/B setting (2) physical connector ID (5:3) reserved (7:6)
+}ATOM_MULTIMEDIA_CONFIG_INFO;
+
+/****************************************************************************/ 
+// Structures used in FirmwareInfoTable
+/****************************************************************************/ 
+
+// usBIOSCapability Defintion:
+// Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; 
+// Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; 
+// Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; 
+// Others: Reserved
 #define ATOM_BIOS_INFO_ATOM_FIRMWARE_POSTED         0x0001
 #define ATOM_BIOS_INFO_DUAL_CRTC_SUPPORT            0x0002
 #define ATOM_BIOS_INFO_EXTENDED_DESKTOP_SUPPORT     0x0004
-#define ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT      0x0008
-#define ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT      0x0010
+#define ATOM_BIOS_INFO_MEMORY_CLOCK_SS_SUPPORT      0x0008             // (valid from v1.1 ~v1.4):=1: memclk SS enable, =0 memclk SS disable. 
+#define ATOM_BIOS_INFO_ENGINE_CLOCK_SS_SUPPORT      0x0010             // (valid from v1.1 ~v1.4):=1: engclk SS enable, =0 engclk SS disable. 
 #define ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU         0x0020
 #define ATOM_BIOS_INFO_WMI_SUPPORT                  0x0040
 #define ATOM_BIOS_INFO_PPMODE_ASSIGNGED_BY_SYSTEM   0x0080
@@ -1441,242 +1797,292 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO {
 #define ATOM_BIOS_INFO_HYPERMEMORY_SIZE_MASK        0x1E00
 #define ATOM_BIOS_INFO_VPOST_WITHOUT_FIRST_MODE_SET 0x2000
 #define ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE  0x4000
+#define ATOM_BIOS_INFO_MEMORY_CLOCK_EXT_SS_SUPPORT  0x0008             // (valid from v2.1 ): =1: memclk ss enable with external ss chip
+#define ATOM_BIOS_INFO_ENGINE_CLOCK_EXT_SS_SUPPORT  0x0010             // (valid from v2.1 ): =1: engclk ss enable with external ss chip
 
 #ifndef _H2INC
 
-/* Please don't add or expand this bitfield structure below, this one will retire soon.! */
-typedef struct _ATOM_FIRMWARE_CAPABILITY {
+//Please don't add or expand this bitfield structure below, this one will retire soon.!
+typedef struct _ATOM_FIRMWARE_CAPABILITY
+{
 #if ATOM_BIG_ENDIAN
-       USHORT Reserved:3;
-       USHORT HyperMemory_Size:4;
-       USHORT HyperMemory_Support:1;
-       USHORT PPMode_Assigned:1;
-       USHORT WMI_SUPPORT:1;
-       USHORT GPUControlsBL:1;
-       USHORT EngineClockSS_Support:1;
-       USHORT MemoryClockSS_Support:1;
-       USHORT ExtendedDesktopSupport:1;
-       USHORT DualCRTC_Support:1;
-       USHORT FirmwarePosted:1;
+  USHORT Reserved:3;
+  USHORT HyperMemory_Size:4;
+  USHORT HyperMemory_Support:1;
+  USHORT PPMode_Assigned:1;
+  USHORT WMI_SUPPORT:1;
+  USHORT GPUControlsBL:1;
+  USHORT EngineClockSS_Support:1;
+  USHORT MemoryClockSS_Support:1;
+  USHORT ExtendedDesktopSupport:1;
+  USHORT DualCRTC_Support:1;
+  USHORT FirmwarePosted:1;
 #else
-       USHORT FirmwarePosted:1;
-       USHORT DualCRTC_Support:1;
-       USHORT ExtendedDesktopSupport:1;
-       USHORT MemoryClockSS_Support:1;
-       USHORT EngineClockSS_Support:1;
-       USHORT GPUControlsBL:1;
-       USHORT WMI_SUPPORT:1;
-       USHORT PPMode_Assigned:1;
-       USHORT HyperMemory_Support:1;
-       USHORT HyperMemory_Size:4;
-       USHORT Reserved:3;
+  USHORT FirmwarePosted:1;
+  USHORT DualCRTC_Support:1;
+  USHORT ExtendedDesktopSupport:1;
+  USHORT MemoryClockSS_Support:1;
+  USHORT EngineClockSS_Support:1;
+  USHORT GPUControlsBL:1;
+  USHORT WMI_SUPPORT:1;
+  USHORT PPMode_Assigned:1;
+  USHORT HyperMemory_Support:1;
+  USHORT HyperMemory_Size:4;
+  USHORT Reserved:3;
 #endif
-} ATOM_FIRMWARE_CAPABILITY;
+}ATOM_FIRMWARE_CAPABILITY;
 
-typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS {
-       ATOM_FIRMWARE_CAPABILITY sbfAccess;
-       USHORT susAccess;
-} ATOM_FIRMWARE_CAPABILITY_ACCESS;
+typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS
+{
+  ATOM_FIRMWARE_CAPABILITY sbfAccess;
+  USHORT                   susAccess;
+}ATOM_FIRMWARE_CAPABILITY_ACCESS;
 
 #else
 
-typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS {
-       USHORT susAccess;
-} ATOM_FIRMWARE_CAPABILITY_ACCESS;
+typedef union _ATOM_FIRMWARE_CAPABILITY_ACCESS
+{
+  USHORT                   susAccess;
+}ATOM_FIRMWARE_CAPABILITY_ACCESS;
 
 #endif
 
-typedef struct _ATOM_FIRMWARE_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulFirmwareRevision;
-       ULONG ulDefaultEngineClock;     /* In 10Khz unit */
-       ULONG ulDefaultMemoryClock;     /* In 10Khz unit */
-       ULONG ulDriverTargetEngineClock;        /* In 10Khz unit */
-       ULONG ulDriverTargetMemoryClock;        /* In 10Khz unit */
-       ULONG ulMaxEngineClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxMemoryClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxPixelClockPLL_Output;        /* In 10Khz unit */
-       ULONG ulASICMaxEngineClock;     /* In 10Khz unit */
-       ULONG ulASICMaxMemoryClock;     /* In 10Khz unit */
-       UCHAR ucASICMaxTemperature;
-       UCHAR ucPadding[3];     /* Don't use them */
-       ULONG aulReservedForBIOS[3];    /* Don't use them */
-       USHORT usMinEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMaxPixelClock; /* In 10Khz unit, Max.  Pclk */
-       USHORT usMinPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMaxPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMinPixelClockPLL_Output;       /* In 10Khz unit, the definitions above can't change!!! */
-       ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
-       USHORT usReferenceClock;        /* In 10Khz unit */
-       USHORT usPM_RTS_Location;       /* RTS PM4 starting location in ROM in 1Kb unit */
-       UCHAR ucPM_RTS_StreamSize;      /* RTS PM4 packets in Kb unit */
-       UCHAR ucDesign_ID;      /* Indicate what is the board design */
-       UCHAR ucMemoryModule_ID;        /* Indicate what is the board design */
-} ATOM_FIRMWARE_INFO;
-
-typedef struct _ATOM_FIRMWARE_INFO_V1_2 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulFirmwareRevision;
-       ULONG ulDefaultEngineClock;     /* In 10Khz unit */
-       ULONG ulDefaultMemoryClock;     /* In 10Khz unit */
-       ULONG ulDriverTargetEngineClock;        /* In 10Khz unit */
-       ULONG ulDriverTargetMemoryClock;        /* In 10Khz unit */
-       ULONG ulMaxEngineClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxMemoryClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxPixelClockPLL_Output;        /* In 10Khz unit */
-       ULONG ulASICMaxEngineClock;     /* In 10Khz unit */
-       ULONG ulASICMaxMemoryClock;     /* In 10Khz unit */
-       UCHAR ucASICMaxTemperature;
-       UCHAR ucMinAllowedBL_Level;
-       UCHAR ucPadding[2];     /* Don't use them */
-       ULONG aulReservedForBIOS[2];    /* Don't use them */
-       ULONG ulMinPixelClockPLL_Output;        /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMaxPixelClock; /* In 10Khz unit, Max.  Pclk */
-       USHORT usMinPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMaxPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMinPixelClockPLL_Output;       /* In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output */
-       ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
-       USHORT usReferenceClock;        /* In 10Khz unit */
-       USHORT usPM_RTS_Location;       /* RTS PM4 starting location in ROM in 1Kb unit */
-       UCHAR ucPM_RTS_StreamSize;      /* RTS PM4 packets in Kb unit */
-       UCHAR ucDesign_ID;      /* Indicate what is the board design */
-       UCHAR ucMemoryModule_ID;        /* Indicate what is the board design */
-} ATOM_FIRMWARE_INFO_V1_2;
-
-typedef struct _ATOM_FIRMWARE_INFO_V1_3 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulFirmwareRevision;
-       ULONG ulDefaultEngineClock;     /* In 10Khz unit */
-       ULONG ulDefaultMemoryClock;     /* In 10Khz unit */
-       ULONG ulDriverTargetEngineClock;        /* In 10Khz unit */
-       ULONG ulDriverTargetMemoryClock;        /* In 10Khz unit */
-       ULONG ulMaxEngineClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxMemoryClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxPixelClockPLL_Output;        /* In 10Khz unit */
-       ULONG ulASICMaxEngineClock;     /* In 10Khz unit */
-       ULONG ulASICMaxMemoryClock;     /* In 10Khz unit */
-       UCHAR ucASICMaxTemperature;
-       UCHAR ucMinAllowedBL_Level;
-       UCHAR ucPadding[2];     /* Don't use them */
-       ULONG aulReservedForBIOS;       /* Don't use them */
-       ULONG ul3DAccelerationEngineClock;      /* In 10Khz unit */
-       ULONG ulMinPixelClockPLL_Output;        /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMaxPixelClock; /* In 10Khz unit, Max.  Pclk */
-       USHORT usMinPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMaxPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMinPixelClockPLL_Output;       /* In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output */
-       ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
-       USHORT usReferenceClock;        /* In 10Khz unit */
-       USHORT usPM_RTS_Location;       /* RTS PM4 starting location in ROM in 1Kb unit */
-       UCHAR ucPM_RTS_StreamSize;      /* RTS PM4 packets in Kb unit */
-       UCHAR ucDesign_ID;      /* Indicate what is the board design */
-       UCHAR ucMemoryModule_ID;        /* Indicate what is the board design */
-} ATOM_FIRMWARE_INFO_V1_3;
-
-typedef struct _ATOM_FIRMWARE_INFO_V1_4 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulFirmwareRevision;
-       ULONG ulDefaultEngineClock;     /* In 10Khz unit */
-       ULONG ulDefaultMemoryClock;     /* In 10Khz unit */
-       ULONG ulDriverTargetEngineClock;        /* In 10Khz unit */
-       ULONG ulDriverTargetMemoryClock;        /* In 10Khz unit */
-       ULONG ulMaxEngineClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxMemoryClockPLL_Output;       /* In 10Khz unit */
-       ULONG ulMaxPixelClockPLL_Output;        /* In 10Khz unit */
-       ULONG ulASICMaxEngineClock;     /* In 10Khz unit */
-       ULONG ulASICMaxMemoryClock;     /* In 10Khz unit */
-       UCHAR ucASICMaxTemperature;
-       UCHAR ucMinAllowedBL_Level;
-       USHORT usBootUpVDDCVoltage;     /* In MV unit */
-       USHORT usLcdMinPixelClockPLL_Output;    /*  In MHz unit */
-       USHORT usLcdMaxPixelClockPLL_Output;    /*  In MHz unit */
-       ULONG ul3DAccelerationEngineClock;      /* In 10Khz unit */
-       ULONG ulMinPixelClockPLL_Output;        /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxEngineClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinEngineClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMaxMemoryClockPLL_Input;       /* In 10Khz unit */
-       USHORT usMinMemoryClockPLL_Output;      /* In 10Khz unit */
-       USHORT usMaxPixelClock; /* In 10Khz unit, Max.  Pclk */
-       USHORT usMinPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMaxPixelClockPLL_Input;        /* In 10Khz unit */
-       USHORT usMinPixelClockPLL_Output;       /* In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output */
-       ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
-       USHORT usReferenceClock;        /* In 10Khz unit */
-       USHORT usPM_RTS_Location;       /* RTS PM4 starting location in ROM in 1Kb unit */
-       UCHAR ucPM_RTS_StreamSize;      /* RTS PM4 packets in Kb unit */
-       UCHAR ucDesign_ID;      /* Indicate what is the board design */
-       UCHAR ucMemoryModule_ID;        /* Indicate what is the board design */
-} ATOM_FIRMWARE_INFO_V1_4;
-
-#define ATOM_FIRMWARE_INFO_LAST  ATOM_FIRMWARE_INFO_V1_4
-
-/****************************************************************************/
-/*  Structures used in IntegratedSystemInfoTable */
-/****************************************************************************/
+typedef struct _ATOM_FIRMWARE_INFO
+{
+  ATOM_COMMON_TABLE_HEADER        sHeader; 
+  ULONG                           ulFirmwareRevision;
+  ULONG                           ulDefaultEngineClock;       //In 10Khz unit
+  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit
+  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit
+  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit
+  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit
+  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit
+  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit
+  UCHAR                           ucASICMaxTemperature;
+  UCHAR                           ucPadding[3];               //Don't use them
+  ULONG                           aulReservedForBIOS[3];      //Don't use them
+  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit
+  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk
+  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit, the definitions above can't change!!!
+  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
+  USHORT                          usReferenceClock;           //In 10Khz unit  
+  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit 
+  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit
+  UCHAR                           ucDesign_ID;                //Indicate what is the board design
+  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design
+}ATOM_FIRMWARE_INFO;
+
+typedef struct _ATOM_FIRMWARE_INFO_V1_2
+{
+  ATOM_COMMON_TABLE_HEADER        sHeader; 
+  ULONG                           ulFirmwareRevision;
+  ULONG                           ulDefaultEngineClock;       //In 10Khz unit
+  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit
+  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit
+  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit
+  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit
+  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit
+  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit
+  UCHAR                           ucASICMaxTemperature;
+  UCHAR                           ucMinAllowedBL_Level;
+  UCHAR                           ucPadding[2];               //Don't use them
+  ULONG                           aulReservedForBIOS[2];      //Don't use them
+  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit
+  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk
+  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output
+  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
+  USHORT                          usReferenceClock;           //In 10Khz unit  
+  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit 
+  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit
+  UCHAR                           ucDesign_ID;                //Indicate what is the board design
+  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design
+}ATOM_FIRMWARE_INFO_V1_2;
+
+typedef struct _ATOM_FIRMWARE_INFO_V1_3
+{
+  ATOM_COMMON_TABLE_HEADER        sHeader; 
+  ULONG                           ulFirmwareRevision;
+  ULONG                           ulDefaultEngineClock;       //In 10Khz unit
+  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit
+  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit
+  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit
+  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit
+  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit
+  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit
+  UCHAR                           ucASICMaxTemperature;
+  UCHAR                           ucMinAllowedBL_Level;
+  UCHAR                           ucPadding[2];               //Don't use them
+  ULONG                           aulReservedForBIOS;         //Don't use them
+  ULONG                           ul3DAccelerationEngineClock;//In 10Khz unit
+  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit
+  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk
+  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output
+  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
+  USHORT                          usReferenceClock;           //In 10Khz unit  
+  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit 
+  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit
+  UCHAR                           ucDesign_ID;                //Indicate what is the board design
+  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design
+}ATOM_FIRMWARE_INFO_V1_3;
+
+typedef struct _ATOM_FIRMWARE_INFO_V1_4
+{
+  ATOM_COMMON_TABLE_HEADER        sHeader; 
+  ULONG                           ulFirmwareRevision;
+  ULONG                           ulDefaultEngineClock;       //In 10Khz unit
+  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit
+  ULONG                           ulDriverTargetEngineClock;  //In 10Khz unit
+  ULONG                           ulDriverTargetMemoryClock;  //In 10Khz unit
+  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit
+  ULONG                           ulASICMaxEngineClock;       //In 10Khz unit
+  ULONG                           ulASICMaxMemoryClock;       //In 10Khz unit
+  UCHAR                           ucASICMaxTemperature;
+  UCHAR                           ucMinAllowedBL_Level;
+  USHORT                          usBootUpVDDCVoltage;        //In MV unit
+  USHORT                          usLcdMinPixelClockPLL_Output; // In MHz unit
+  USHORT                          usLcdMaxPixelClockPLL_Output; // In MHz unit
+  ULONG                           ul3DAccelerationEngineClock;//In 10Khz unit
+  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit
+  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk
+  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output
+  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
+  USHORT                          usReferenceClock;           //In 10Khz unit  
+  USHORT                          usPM_RTS_Location;          //RTS PM4 starting location in ROM in 1Kb unit 
+  UCHAR                           ucPM_RTS_StreamSize;        //RTS PM4 packets in Kb unit
+  UCHAR                           ucDesign_ID;                //Indicate what is the board design
+  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design
+}ATOM_FIRMWARE_INFO_V1_4;
+
+//the structure below to be used from Cypress
+typedef struct _ATOM_FIRMWARE_INFO_V2_1
+{
+  ATOM_COMMON_TABLE_HEADER        sHeader; 
+  ULONG                           ulFirmwareRevision;
+  ULONG                           ulDefaultEngineClock;       //In 10Khz unit
+  ULONG                           ulDefaultMemoryClock;       //In 10Khz unit
+  ULONG                           ulReserved1;
+  ULONG                           ulReserved2;
+  ULONG                           ulMaxEngineClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxMemoryClockPLL_Output; //In 10Khz unit
+  ULONG                           ulMaxPixelClockPLL_Output;  //In 10Khz unit
+  ULONG                           ulBinaryAlteredInfo;        //Was ulASICMaxEngineClock
+  ULONG                           ulDefaultDispEngineClkFreq; //In 10Khz unit
+  UCHAR                           ucReserved1;                //Was ucASICMaxTemperature;
+  UCHAR                           ucMinAllowedBL_Level;
+  USHORT                          usBootUpVDDCVoltage;        //In MV unit
+  USHORT                          usLcdMinPixelClockPLL_Output; // In MHz unit
+  USHORT                          usLcdMaxPixelClockPLL_Output; // In MHz unit
+  ULONG                           ulReserved4;                //Was ulAsicMaximumVoltage
+  ULONG                           ulMinPixelClockPLL_Output;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxEngineClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinEngineClockPLL_Output; //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMaxMemoryClockPLL_Input;  //In 10Khz unit
+  USHORT                          usMinMemoryClockPLL_Output; //In 10Khz unit
+  USHORT                          usMaxPixelClock;            //In 10Khz unit, Max.  Pclk
+  USHORT                          usMinPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMaxPixelClockPLL_Input;   //In 10Khz unit
+  USHORT                          usMinPixelClockPLL_Output;  //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output
+  ATOM_FIRMWARE_CAPABILITY_ACCESS usFirmwareCapability;
+  USHORT                          usCoreReferenceClock;       //In 10Khz unit  
+  USHORT                          usMemoryReferenceClock;     //In 10Khz unit  
+  USHORT                          usUniphyDPModeExtClkFreq;   //In 10Khz unit, if it is 0, In DP Mode Uniphy Input clock from internal PPLL, otherwise Input clock from external Spread clock
+  UCHAR                           ucMemoryModule_ID;          //Indicate what is the board design
+  UCHAR                           ucReserved4[3];
+}ATOM_FIRMWARE_INFO_V2_1;
+
+
+#define ATOM_FIRMWARE_INFO_LAST  ATOM_FIRMWARE_INFO_V2_1
+
+/****************************************************************************/ 
+// Structures used in IntegratedSystemInfoTable
+/****************************************************************************/ 
 #define IGP_CAP_FLAG_DYNAMIC_CLOCK_EN      0x2
 #define IGP_CAP_FLAG_AC_CARD               0x4
 #define IGP_CAP_FLAG_SDVO_CARD             0x8
 #define IGP_CAP_FLAG_POSTDIV_BY_2_MODE     0x10
 
-typedef struct _ATOM_INTEGRATED_SYSTEM_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulBootUpEngineClock;      /* in 10kHz unit */
-       ULONG ulBootUpMemoryClock;      /* in 10kHz unit */
-       ULONG ulMaxSystemMemoryClock;   /* in 10kHz unit */
-       ULONG ulMinSystemMemoryClock;   /* in 10kHz unit */
-       UCHAR ucNumberOfCyclesInPeriodHi;
-       UCHAR ucLCDTimingSel;   /* =0:not valid.!=0 sel this timing descriptor from LCD EDID. */
-       USHORT usReserved1;
-       USHORT usInterNBVoltageLow;     /* An intermidiate PMW value to set the voltage */
-       USHORT usInterNBVoltageHigh;    /* Another intermidiate PMW value to set the voltage */
-       ULONG ulReserved[2];
-
-       USHORT usFSBClock;      /* In MHz unit */
-       USHORT usCapabilityFlag;        /* Bit0=1 indicates the fake HDMI support,Bit1=0/1 for Dynamic clocking dis/enable */
-       /* Bit[3:2]== 0:No PCIE card, 1:AC card, 2:SDVO card */
-       /* Bit[4]==1: P/2 mode, ==0: P/1 mode */
-       USHORT usPCIENBCfgReg7; /* bit[7:0]=MUX_Sel, bit[9:8]=MUX_SEL_LEVEL2, bit[10]=Lane_Reversal */
-       USHORT usK8MemoryClock; /* in MHz unit */
-       USHORT usK8SyncStartDelay;      /* in 0.01 us unit */
-       USHORT usK8DataReturnTime;      /* in 0.01 us unit */
-       UCHAR ucMaxNBVoltage;
-       UCHAR ucMinNBVoltage;
-       UCHAR ucMemoryType;     /* [7:4]=1:DDR1;=2:DDR2;=3:DDR3.[3:0] is reserved */
-       UCHAR ucNumberOfCyclesInPeriod; /* CG.FVTHROT_PWM_CTRL_REG0.NumberOfCyclesInPeriod */
-       UCHAR ucStartingPWM_HighTime;   /* CG.FVTHROT_PWM_CTRL_REG0.StartingPWM_HighTime */
-       UCHAR ucHTLinkWidth;    /* 16 bit vs. 8 bit */
-       UCHAR ucMaxNBVoltageHigh;
-       UCHAR ucMinNBVoltageHigh;
-} ATOM_INTEGRATED_SYSTEM_INFO;
+typedef struct _ATOM_INTEGRATED_SYSTEM_INFO
+{
+  ATOM_COMMON_TABLE_HEADER        sHeader; 
+  ULONG                                  ulBootUpEngineClock;              //in 10kHz unit
+  ULONG                                  ulBootUpMemoryClock;              //in 10kHz unit
+  ULONG                                  ulMaxSystemMemoryClock;           //in 10kHz unit
+  ULONG                                  ulMinSystemMemoryClock;           //in 10kHz unit
+  UCHAR                           ucNumberOfCyclesInPeriodHi;
+  UCHAR                           ucLCDTimingSel;             //=0:not valid.!=0 sel this timing descriptor from LCD EDID.
+  USHORT                          usReserved1;
+  USHORT                          usInterNBVoltageLow;        //An intermidiate PMW value to set the voltage 
+  USHORT                          usInterNBVoltageHigh;       //Another intermidiate PMW value to set the voltage 
+  ULONG                                  ulReserved[2];
+
+  USHORT                               usFSBClock;                                 //In MHz unit
+  USHORT                          usCapabilityFlag;                    //Bit0=1 indicates the fake HDMI support,Bit1=0/1 for Dynamic clocking dis/enable
+                                                                                                                                                             //Bit[3:2]== 0:No PCIE card, 1:AC card, 2:SDVO card
+                                                              //Bit[4]==1: P/2 mode, ==0: P/1 mode
+  USHORT                               usPCIENBCfgReg7;                                    //bit[7:0]=MUX_Sel, bit[9:8]=MUX_SEL_LEVEL2, bit[10]=Lane_Reversal
+  USHORT                               usK8MemoryClock;            //in MHz unit
+  USHORT                               usK8SyncStartDelay;         //in 0.01 us unit
+  USHORT                               usK8DataReturnTime;         //in 0.01 us unit
+  UCHAR                           ucMaxNBVoltage;
+  UCHAR                           ucMinNBVoltage;
+  UCHAR                           ucMemoryType;                                              //[7:4]=1:DDR1;=2:DDR2;=3:DDR3.[3:0] is reserved
+  UCHAR                           ucNumberOfCyclesInPeriod;            //CG.FVTHROT_PWM_CTRL_REG0.NumberOfCyclesInPeriod 
+  UCHAR                           ucStartingPWM_HighTime;     //CG.FVTHROT_PWM_CTRL_REG0.StartingPWM_HighTime
+  UCHAR                           ucHTLinkWidth;              //16 bit vs. 8 bit
+  UCHAR                           ucMaxNBVoltageHigh;    
+  UCHAR                           ucMinNBVoltageHigh;
+}ATOM_INTEGRATED_SYSTEM_INFO;
 
 /* Explanation on entries in ATOM_INTEGRATED_SYSTEM_INFO
-ulBootUpMemoryClock:    For Intel IGP,it's the UMA system memory clock
+ulBootUpMemoryClock:    For Intel IGP,it's the UMA system memory clock 
                         For AMD IGP,it's 0 if no SidePort memory installed or it's the boot-up SidePort memory clock
 ulMaxSystemMemoryClock: For Intel IGP,it's the Max freq from memory SPD if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0
                         For AMD IGP,for now this can be 0
-ulMinSystemMemoryClock: For Intel IGP,it's 133MHz if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0
+ulMinSystemMemoryClock: For Intel IGP,it's 133MHz if memory runs in ASYNC mode or otherwise (SYNC mode) it's 0 
                         For AMD IGP,for now this can be 0
 
-usFSBClock:             For Intel IGP,it's FSB Freq
+usFSBClock:             For Intel IGP,it's FSB Freq 
                         For AMD IGP,it's HT Link Speed
 
 usK8MemoryClock:        For AMD IGP only. For RevF CPU, set it to 200
@@ -1687,98 +2093,113 @@ VC:Voltage Control
 ucMaxNBVoltage:         Voltage regulator dependent PWM value. Low 8 bits of the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all.
 ucMinNBVoltage:         Voltage regulator dependent PWM value. Low 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all.
 
-ucNumberOfCyclesInPeriod:   Indicate how many cycles when PWM duty is 100%. low 8 bits of the value.
-ucNumberOfCyclesInPeriodHi: Indicate how many cycles when PWM duty is 100%. high 8 bits of the value.If the PWM has an inverter,set bit [7]==1,otherwise set it 0
+ucNumberOfCyclesInPeriod:   Indicate how many cycles when PWM duty is 100%. low 8 bits of the value. 
+ucNumberOfCyclesInPeriodHi: Indicate how many cycles when PWM duty is 100%. high 8 bits of the value.If the PWM has an inverter,set bit [7]==1,otherwise set it 0 
 
 ucMaxNBVoltageHigh:     Voltage regulator dependent PWM value. High 8 bits of  the value for the max voltage.Set this one to 0xFF if VC without PWM. Set this to 0x0 if no VC at all.
 ucMinNBVoltageHigh:     Voltage regulator dependent PWM value. High 8 bits of the value for the min voltage.Set this one to 0x00 if VC without PWM or no VC at all.
 
+
 usInterNBVoltageLow:    Voltage regulator dependent PWM value. The value makes the the voltage >=Min NB voltage but <=InterNBVoltageHigh. Set this to 0x0000 if VC without PWM or no VC at all.
 usInterNBVoltageHigh:   Voltage regulator dependent PWM value. The value makes the the voltage >=InterNBVoltageLow but <=Max NB voltage.Set this to 0x0000 if VC without PWM or no VC at all.
 */
 
+
 /*
 The following IGP table is introduced from RS780, which is supposed to be put by SBIOS in FB before IGP VBIOS starts VPOST;
-Then VBIOS will copy the whole structure to its image so all GPU SW components can access this data structure to get whatever they need.
+Then VBIOS will copy the whole structure to its image so all GPU SW components can access this data structure to get whatever they need. 
 The enough reservation should allow us to never change table revisions. Whenever needed, a GPU SW component can use reserved portion for new data entries.
 
 SW components can access the IGP system infor structure in the same way as before
 */
 
-typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ULONG ulBootUpEngineClock;      /* in 10kHz unit */
-       ULONG ulReserved1[2];   /* must be 0x0 for the reserved */
-       ULONG ulBootUpUMAClock; /* in 10kHz unit */
-       ULONG ulBootUpSidePortClock;    /* in 10kHz unit */
-       ULONG ulMinSidePortClock;       /* in 10kHz unit */
-       ULONG ulReserved2[6];   /* must be 0x0 for the reserved */
-       ULONG ulSystemConfig;   /* see explanation below */
-       ULONG ulBootUpReqDisplayVector;
-       ULONG ulOtherDisplayMisc;
-       ULONG ulDDISlot1Config;
-       ULONG ulDDISlot2Config;
-       UCHAR ucMemoryType;     /* [3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved */
-       UCHAR ucUMAChannelNumber;
-       UCHAR ucDockingPinBit;
-       UCHAR ucDockingPinPolarity;
-       ULONG ulDockingPinCFGInfo;
-       ULONG ulCPUCapInfo;
-       USHORT usNumberOfCyclesInPeriod;
-       USHORT usMaxNBVoltage;
-       USHORT usMinNBVoltage;
-       USHORT usBootUpNBVoltage;
-       ULONG ulHTLinkFreq;     /* in 10Khz */
-       USHORT usMinHTLinkWidth;
-       USHORT usMaxHTLinkWidth;
-       USHORT usUMASyncStartDelay;
-       USHORT usUMADataReturnTime;
-       USHORT usLinkStatusZeroTime;
-       USHORT usReserved;
-       ULONG ulHighVoltageHTLinkFreq;  /*  in 10Khz */
-       ULONG ulLowVoltageHTLinkFreq;   /*  in 10Khz */
-       USHORT usMaxUpStreamHTLinkWidth;
-       USHORT usMaxDownStreamHTLinkWidth;
-       USHORT usMinUpStreamHTLinkWidth;
-       USHORT usMinDownStreamHTLinkWidth;
-       ULONG ulReserved3[97];  /* must be 0x0 */
-} ATOM_INTEGRATED_SYSTEM_INFO_V2;
+
+typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V2
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+  ULONG                             ulBootUpEngineClock;       //in 10kHz unit
+  ULONG                             ulReserved1[2];            //must be 0x0 for the reserved
+  ULONG                             ulBootUpUMAClock;          //in 10kHz unit
+  ULONG                             ulBootUpSidePortClock;     //in 10kHz unit
+  ULONG                             ulMinSidePortClock;        //in 10kHz unit
+  ULONG                             ulReserved2[6];            //must be 0x0 for the reserved
+  ULONG                      ulSystemConfig;            //see explanation below
+  ULONG                      ulBootUpReqDisplayVector;
+  ULONG                      ulOtherDisplayMisc;
+  ULONG                      ulDDISlot1Config;
+  ULONG                      ulDDISlot2Config;
+  UCHAR                      ucMemoryType;              //[3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved
+  UCHAR                      ucUMAChannelNumber;
+  UCHAR                      ucDockingPinBit;
+  UCHAR                      ucDockingPinPolarity;
+  ULONG                      ulDockingPinCFGInfo;
+  ULONG                      ulCPUCapInfo;
+  USHORT                     usNumberOfCyclesInPeriod;
+  USHORT                     usMaxNBVoltage;
+  USHORT                     usMinNBVoltage;
+  USHORT                     usBootUpNBVoltage;
+  ULONG                      ulHTLinkFreq;              //in 10Khz
+  USHORT                     usMinHTLinkWidth;
+  USHORT                     usMaxHTLinkWidth;
+  USHORT                     usUMASyncStartDelay;
+  USHORT                     usUMADataReturnTime;
+  USHORT                     usLinkStatusZeroTime;
+  USHORT                     usDACEfuse;                               //for storing badgap value (for RS880 only)
+  ULONG                      ulHighVoltageHTLinkFreq;     // in 10Khz
+  ULONG                      ulLowVoltageHTLinkFreq;      // in 10Khz
+  USHORT                     usMaxUpStreamHTLinkWidth;
+  USHORT                     usMaxDownStreamHTLinkWidth;
+  USHORT                     usMinUpStreamHTLinkWidth;
+  USHORT                     usMinDownStreamHTLinkWidth;
+  USHORT                     usFirmwareVersion;         //0 means FW is not supported. Otherwise it's the FW version loaded by SBIOS and driver should enable FW.
+  USHORT                     usFullT0Time;             // Input to calculate minimum HT link change time required by NB P-State. Unit is 0.01us.
+  ULONG                      ulReserved3[96];          //must be 0x0
+}ATOM_INTEGRATED_SYSTEM_INFO_V2;   
 
 /*
 ulBootUpEngineClock:   Boot-up Engine Clock in 10Khz;
 ulBootUpUMAClock:      Boot-up UMA Clock in 10Khz; it must be 0x0 when UMA is not present
 ulBootUpSidePortClock: Boot-up SidePort Clock in 10Khz; it must be 0x0 when SidePort Memory is not present,this could be equal to or less than maximum supported Sideport memory clock
 
-ulSystemConfig:
-Bit[0]=1: PowerExpress mode =0 Non-PowerExpress mode;
+ulSystemConfig:  
+Bit[0]=1: PowerExpress mode =0 Non-PowerExpress mode; 
 Bit[1]=1: system boots up at AMD overdrived state or user customized  mode. In this case, driver will just stick to this boot-up mode. No other PowerPlay state
       =0: system boots up at driver control state. Power state depends on PowerPlay table.
 Bit[2]=1: PWM method is used on NB voltage control. =0: GPIO method is used.
 Bit[3]=1: Only one power state(Performance) will be supported.
       =0: Multiple power states supported from PowerPlay table.
-Bit[4]=1: CLMC is supported and enabled on current system.
-      =0: CLMC is not supported or enabled on current system. SBIOS need to support HT link/freq change through ATIF interface.
-Bit[5]=1: Enable CDLW for all driver control power states. Max HT width is from SBIOS, while Min HT width is determined by display requirement.
+Bit[4]=1: CLMC is supported and enabled on current system. 
+      =0: CLMC is not supported or enabled on current system. SBIOS need to support HT link/freq change through ATIF interface.  
+Bit[5]=1: Enable CDLW for all driver control power states. Max HT width is from SBIOS, while Min HT width is determined by display requirement.  
       =0: CDLW is disabled. If CLMC is enabled case, Min HT width will be set equal to Max HT width. If CLMC disabled case, Max HT width will be applied.
 Bit[6]=1: High Voltage requested for all power states. In this case, voltage will be forced at 1.1v and powerplay table voltage drop/throttling request will be ignored.
       =0: Voltage settings is determined by powerplay table.
 Bit[7]=1: Enable CLMC as hybrid Mode. CDLD and CILR will be disabled in this case and we're using legacy C1E. This is workaround for CPU(Griffin) performance issue.
       =0: Enable CLMC as regular mode, CDLD and CILR will be enabled.
+Bit[8]=1: CDLF is supported and enabled on current system.
+      =0: CDLF is not supported or enabled on current system.
+Bit[9]=1: DLL Shut Down feature is enabled on current system.
+      =0: DLL Shut Down feature is not enabled or supported on current system.
 
 ulBootUpReqDisplayVector: This dword is a bit vector indicates what display devices are requested during boot-up. Refer to ATOM_DEVICE_xxx_SUPPORT for the bit vector definitions.
 
 ulOtherDisplayMisc: [15:8]- Bootup LCD Expansion selection; 0-center, 1-full panel size expansion;
-                                     [7:0] - BootupTV standard selection; This is a bit vector to indicate what TV standards are supported by the system. Refer to ucTVSuppportedStd definition;
+                                     [7:0] - BootupTV standard selection; This is a bit vector to indicate what TV standards are supported by the system. Refer to ucTVSupportedStd definition;
 
 ulDDISlot1Config: Describes the PCIE lane configuration on this DDI PCIE slot (ADD2 card) or connector (Mobile design).
       [3:0]  - Bit vector to indicate PCIE lane config of the DDI slot/connector on chassis (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12)
-                       [7:4]  - Bit vector to indicate PCIE lane config of the same DDI slot/connector on docking station (bit 0=1 lane 3:0; bit 1=1 lane 7:4; bit 2=1 lane 11:8; bit 3=1 lane 15:12)
-                       [15:8] - Lane configuration attribute;
+                       [7:4]  - Bit vector to indicate PCIE lane config of the same DDI slot/connector on docking station (bit 4=1 lane 3:0; bit 5=1 lane 7:4; bit 6=1 lane 11:8; bit 7=1 lane 15:12)
+      When a DDI connector is not "paired" (meaming two connections mutualexclusive on chassis or docking, only one of them can be connected at one time.
+      in both chassis and docking, SBIOS has to duplicate the same PCIE lane info from chassis to docking or vice versa. For example:
+      one DDI connector is only populated in docking with PCIE lane 8-11, but there is no paired connection on chassis, SBIOS has to copy bit 6 to bit 2.
+
+                       [15:8] - Lane configuration attribute; 
       [23:16]- Connector type, possible value:
                CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D
                CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D
                CONNECTOR_OBJECT_ID_HDMI_TYPE_A
                CONNECTOR_OBJECT_ID_DISPLAYPORT
+               CONNECTOR_OBJECT_ID_eDP
                        [31:24]- Reserved
 
 ulDDISlot2Config: Same as Slot1.
@@ -1787,29 +2208,31 @@ For IGP, Hypermemory is the only memory type showed in CCC.
 
 ucUMAChannelNumber:  how many channels for the UMA;
 
-ulDockingPinCFGInfo: [15:0]-Bus/Device/Function # to CFG to read this Docking Pin; [31:16]-reg offset in CFG to read this pin
+ulDockingPinCFGInfo: [15:0]-Bus/Device/Function # to CFG to read this Docking Pin; [31:16]-reg offset in CFG to read this pin 
 ucDockingPinBit:     which bit in this register to read the pin status;
 ucDockingPinPolarity:Polarity of the pin when docked;
 
 ulCPUCapInfo:        [7:0]=1:Griffin;[7:0]=2:Greyhound;[7:0]=3:K8, other bits reserved for now and must be 0x0
 
 usNumberOfCyclesInPeriod:Indicate how many cycles when PWM duty is 100%.
-usMaxNBVoltage:Max. voltage control value in either PWM or GPIO mode.
+
+usMaxNBVoltage:Max. voltage control value in either PWM or GPIO mode. 
 usMinNBVoltage:Min. voltage control value in either PWM or GPIO mode.
                     GPIO mode: both usMaxNBVoltage & usMinNBVoltage have a valid value ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE=0
                     PWM mode: both usMaxNBVoltage & usMinNBVoltage have a valid value ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE=1
                     GPU SW don't control mode: usMaxNBVoltage & usMinNBVoltage=0 and no care about ulSystemConfig.SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE
+
 usBootUpNBVoltage:Boot-up voltage regulator dependent PWM value.
 
 ulHTLinkFreq:       Bootup HT link Frequency in 10Khz.
-usMinHTLinkWidth:   Bootup minimum HT link width. If CDLW disabled, this is equal to usMaxHTLinkWidth.
-                    If CDLW enabled, both upstream and downstream width should be the same during bootup.
-usMaxHTLinkWidth:   Bootup maximum HT link width. If CDLW disabled, this is equal to usMinHTLinkWidth.
+usMinHTLinkWidth:   Bootup minimum HT link width. If CDLW disabled, this is equal to usMaxHTLinkWidth. 
                     If CDLW enabled, both upstream and downstream width should be the same during bootup.
+usMaxHTLinkWidth:   Bootup maximum HT link width. If CDLW disabled, this is equal to usMinHTLinkWidth. 
+                    If CDLW enabled, both upstream and downstream width should be the same during bootup.  
 
-usUMASyncStartDelay: Memory access latency, required for watermark calculation
+usUMASyncStartDelay: Memory access latency, required for watermark calculation 
 usUMADataReturnTime: Memory access latency, required for watermark calculation
-usLinkStatusZeroTime:Memory access latency required for watermark calculation, set this to 0x0 for K8 CPU, set a proper value in 0.01 the unit of us
+usLinkStatusZeroTime:Memory access latency required for watermark calculation, set this to 0x0 for K8 CPU, set a proper value in 0.01 the unit of us 
 for Griffin or Greyhound. SBIOS needs to convert to actual time by:
                      if T0Ttime [5:4]=00b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.1us (0.0 to 1.5us)
                      if T0Ttime [5:4]=01b, then usLinkStatusZeroTime=T0Ttime [3:0]*0.5us (0.0 to 7.5us)
@@ -1817,7 +2240,7 @@ for Griffin or Greyhound. SBIOS needs to convert to actual time by:
                      if T0Ttime [5:4]=11b, and T0Ttime [3:0]=0x0 to 0xa, then usLinkStatusZeroTime=T0Ttime [3:0]*20us (0.0 to 200us)
 
 ulHighVoltageHTLinkFreq:     HT link frequency for power state with low voltage. If boot up runs in HT1, this must be 0.
-                             This must be less than or equal to ulHTLinkFreq(bootup frequency).
+                             This must be less than or equal to ulHTLinkFreq(bootup frequency). 
 ulLowVoltageHTLinkFreq:      HT link frequency for power state with low voltage or voltage scaling 1.0v~1.1v. If boot up runs in HT1, this must be 0.
                              This must be less than or equal to ulHighVoltageHTLinkFreq.
 
@@ -1827,14 +2250,17 @@ usMinUpStreamHTLinkWidth:    Asymmetric link width support in the future, to rep
 usMinDownStreamHTLinkWidth:  same as above.
 */
 
+
 #define SYSTEM_CONFIG_POWEREXPRESS_ENABLE                 0x00000001
 #define SYSTEM_CONFIG_RUN_AT_OVERDRIVE_ENGINE             0x00000002
-#define SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE                  0x00000004
+#define SYSTEM_CONFIG_USE_PWM_ON_VOLTAGE                  0x00000004 
 #define SYSTEM_CONFIG_PERFORMANCE_POWERSTATE_ONLY         0x00000008
 #define SYSTEM_CONFIG_CLMC_ENABLED                        0x00000010
 #define SYSTEM_CONFIG_CDLW_ENABLED                        0x00000020
 #define SYSTEM_CONFIG_HIGH_VOLTAGE_REQUESTED              0x00000040
 #define SYSTEM_CONFIG_CLMC_HYBRID_MODE_ENABLED            0x00000080
+#define SYSTEM_CONFIG_CDLF_ENABLED                        0x00000100
+#define SYSTEM_CONFIG_DLL_SHUTDOWN_ENABLED                0x00000200
 
 #define IGP_DDI_SLOT_LANE_CONFIG_MASK                     0x000000FF
 
@@ -1851,6 +2277,41 @@ usMinDownStreamHTLinkWidth:  same as above.
 
 #define IGP_DDI_SLOT_CONNECTOR_TYPE_MASK                  0x00FF0000
 
+// IntegratedSystemInfoTable new Rev is V5 after V2, because of the real rev of V2 is v1.4. This rev is used for RR
+typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V5
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+  ULONG                             ulBootUpEngineClock;       //in 10kHz unit
+  ULONG                      ulDentistVCOFreq;          //Dentist VCO clock in 10kHz unit, the source of GPU SCLK, LCLK, UCLK and VCLK. 
+  ULONG                      ulLClockFreq;              //GPU Lclk freq in 10kHz unit, have relationship with NCLK in NorthBridge
+  ULONG                             ulBootUpUMAClock;          //in 10kHz unit
+  ULONG                      ulReserved1[8];            //must be 0x0 for the reserved
+  ULONG                      ulBootUpReqDisplayVector;
+  ULONG                      ulOtherDisplayMisc;
+  ULONG                      ulReserved2[4];            //must be 0x0 for the reserved
+  ULONG                      ulSystemConfig;            //TBD
+  ULONG                      ulCPUCapInfo;              //TBD
+  USHORT                     usMaxNBVoltage;            //high NB voltage, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse;
+  USHORT                     usMinNBVoltage;            //low NB voltage, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse;
+  USHORT                     usBootUpNBVoltage;         //boot up NB voltage
+  UCHAR                      ucHtcTmpLmt;               //bit [22:16] of D24F3x64 Hardware Thermal Control (HTC) Register, may not be needed, TBD
+  UCHAR                      ucTjOffset;                //bit [28:22] of D24F3xE4 Thermtrip Status Register,may not be needed, TBD
+  ULONG                      ulReserved3[4];            //must be 0x0 for the reserved
+  ULONG                      ulDDISlot1Config;          //see above ulDDISlot1Config definition
+  ULONG                      ulDDISlot2Config;
+  ULONG                      ulDDISlot3Config;
+  ULONG                      ulDDISlot4Config;
+  ULONG                      ulReserved4[4];            //must be 0x0 for the reserved
+  UCHAR                      ucMemoryType;              //[3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved
+  UCHAR                      ucUMAChannelNumber;
+  USHORT                     usReserved;
+  ULONG                      ulReserved5[4];            //must be 0x0 for the reserved
+  ULONG                      ulCSR_M3_ARB_CNTL_DEFAULT[10];//arrays with values for CSR M3 arbiter for default
+  ULONG                      ulCSR_M3_ARB_CNTL_UVD[10]; //arrays with values for CSR M3 arbiter for UVD playback
+  ULONG                      ulCSR_M3_ARB_CNTL_FS3D[10];//arrays with values for CSR M3 arbiter for Full Screen 3D applications
+  ULONG                      ulReserved6[61];           //must be 0x0
+}ATOM_INTEGRATED_SYSTEM_INFO_V5;   
+
 #define ATOM_CRT_INT_ENCODER1_INDEX                       0x00000000
 #define ATOM_LCD_INT_ENCODER1_INDEX                       0x00000001
 #define ATOM_TV_INT_ENCODER1_INDEX                        0x00000002
@@ -1866,8 +2327,8 @@ usMinDownStreamHTLinkWidth:  same as above.
 #define ATOM_DFP_INT_ENCODER3_INDEX                       0x0000000C
 #define ATOM_DFP_INT_ENCODER4_INDEX                       0x0000000D
 
-/*  define ASIC internal encoder id ( bit vector ) */
-#define ASIC_INT_DAC1_ENCODER_ID                                                                                       0x00
+// define ASIC internal encoder id ( bit vector ), used for CRTC_SourceSelTable
+#define ASIC_INT_DAC1_ENCODER_ID                                                                                       0x00 
 #define ASIC_INT_TV_ENCODER_ID                                                                                                         0x02
 #define ASIC_INT_DIG1_ENCODER_ID                                                                                                       0x03
 #define ASIC_INT_DAC2_ENCODER_ID                                                                                                       0x04
@@ -1875,10 +2336,24 @@ usMinDownStreamHTLinkWidth:  same as above.
 #define ASIC_INT_DVO_ENCODER_ID                                                                                                                0x07
 #define ASIC_INT_DIG2_ENCODER_ID                                                                                                       0x09
 #define ASIC_EXT_DIG_ENCODER_ID                                                                                                                0x05
+#define ASIC_EXT_DIG2_ENCODER_ID                                                                                                       0x08
+#define ASIC_INT_DIG3_ENCODER_ID                                                                                                       0x0a
+#define ASIC_INT_DIG4_ENCODER_ID                                                                                                       0x0b
+#define ASIC_INT_DIG5_ENCODER_ID                                                                                                       0x0c
+#define ASIC_INT_DIG6_ENCODER_ID                                                                                                       0x0d
 
-/* define Encoder attribute */
+//define Encoder attribute
 #define ATOM_ANALOG_ENCODER                                                                                                                            0
-#define ATOM_DIGITAL_ENCODER                                                                                                                   1
+#define ATOM_DIGITAL_ENCODER                                                                                                                   1               
+#define ATOM_DP_ENCODER                                                                                                                              2         
+
+#define ATOM_ENCODER_ENUM_MASK                            0x70
+#define ATOM_ENCODER_ENUM_ID1                             0x00
+#define ATOM_ENCODER_ENUM_ID2                             0x10
+#define ATOM_ENCODER_ENUM_ID3                             0x20
+#define ATOM_ENCODER_ENUM_ID4                             0x30
+#define ATOM_ENCODER_ENUM_ID5                             0x40 
+#define ATOM_ENCODER_ENUM_ID6                             0x50
 
 #define ATOM_DEVICE_CRT1_INDEX                            0x00000000
 #define ATOM_DEVICE_LCD1_INDEX                            0x00000001
@@ -1886,45 +2361,40 @@ usMinDownStreamHTLinkWidth:  same as above.
 #define ATOM_DEVICE_DFP1_INDEX                            0x00000003
 #define ATOM_DEVICE_CRT2_INDEX                            0x00000004
 #define ATOM_DEVICE_LCD2_INDEX                            0x00000005
-#define ATOM_DEVICE_TV2_INDEX                             0x00000006
+#define ATOM_DEVICE_DFP6_INDEX                            0x00000006
 #define ATOM_DEVICE_DFP2_INDEX                            0x00000007
 #define ATOM_DEVICE_CV_INDEX                              0x00000008
-#define ATOM_DEVICE_DFP3_INDEX                                                                                                         0x00000009
-#define ATOM_DEVICE_DFP4_INDEX                                                                                                         0x0000000A
-#define ATOM_DEVICE_DFP5_INDEX                                                                                                         0x0000000B
+#define ATOM_DEVICE_DFP3_INDEX                            0x00000009
+#define ATOM_DEVICE_DFP4_INDEX                            0x0000000A
+#define ATOM_DEVICE_DFP5_INDEX                            0x0000000B
+
 #define ATOM_DEVICE_RESERVEDC_INDEX                       0x0000000C
 #define ATOM_DEVICE_RESERVEDD_INDEX                       0x0000000D
 #define ATOM_DEVICE_RESERVEDE_INDEX                       0x0000000E
 #define ATOM_DEVICE_RESERVEDF_INDEX                       0x0000000F
 #define ATOM_MAX_SUPPORTED_DEVICE_INFO                    (ATOM_DEVICE_DFP3_INDEX+1)
 #define ATOM_MAX_SUPPORTED_DEVICE_INFO_2                  ATOM_MAX_SUPPORTED_DEVICE_INFO
-#define ATOM_MAX_SUPPORTED_DEVICE_INFO_3                  (ATOM_DEVICE_DFP5_INDEX + 1)
+#define ATOM_MAX_SUPPORTED_DEVICE_INFO_3                  (ATOM_DEVICE_DFP5_INDEX + 1 )
 
 #define ATOM_MAX_SUPPORTED_DEVICE                         (ATOM_DEVICE_RESERVEDF_INDEX+1)
 
-#define ATOM_DEVICE_CRT1_SUPPORT                          (0x1L << ATOM_DEVICE_CRT1_INDEX)
-#define ATOM_DEVICE_LCD1_SUPPORT                          (0x1L << ATOM_DEVICE_LCD1_INDEX)
-#define ATOM_DEVICE_TV1_SUPPORT                           (0x1L << ATOM_DEVICE_TV1_INDEX)
-#define ATOM_DEVICE_DFP1_SUPPORT                          (0x1L << ATOM_DEVICE_DFP1_INDEX)
-#define ATOM_DEVICE_CRT2_SUPPORT                          (0x1L << ATOM_DEVICE_CRT2_INDEX)
-#define ATOM_DEVICE_LCD2_SUPPORT                          (0x1L << ATOM_DEVICE_LCD2_INDEX)
-#define ATOM_DEVICE_TV2_SUPPORT                           (0x1L << ATOM_DEVICE_TV2_INDEX)
-#define ATOM_DEVICE_DFP2_SUPPORT                          (0x1L << ATOM_DEVICE_DFP2_INDEX)
-#define ATOM_DEVICE_CV_SUPPORT                            (0x1L << ATOM_DEVICE_CV_INDEX)
-#define ATOM_DEVICE_DFP3_SUPPORT                                                                                                       (0x1L << ATOM_DEVICE_DFP3_INDEX)
-#define ATOM_DEVICE_DFP4_SUPPORT                                                                                                       (0x1L << ATOM_DEVICE_DFP4_INDEX )
-#define ATOM_DEVICE_DFP5_SUPPORT                                                                                                       (0x1L << ATOM_DEVICE_DFP5_INDEX)
-
-#define ATOM_DEVICE_CRT_SUPPORT \
-       (ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT)
-#define ATOM_DEVICE_DFP_SUPPORT \
-       (ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT | \
-        ATOM_DEVICE_DFP3_SUPPORT | ATOM_DEVICE_DFP4_SUPPORT | \
-        ATOM_DEVICE_DFP5_SUPPORT)
-#define ATOM_DEVICE_TV_SUPPORT \
-       (ATOM_DEVICE_TV1_SUPPORT  | ATOM_DEVICE_TV2_SUPPORT)
-#define ATOM_DEVICE_LCD_SUPPORT \
-       (ATOM_DEVICE_LCD1_SUPPORT | ATOM_DEVICE_LCD2_SUPPORT)
+#define ATOM_DEVICE_CRT1_SUPPORT                          (0x1L << ATOM_DEVICE_CRT1_INDEX )
+#define ATOM_DEVICE_LCD1_SUPPORT                          (0x1L << ATOM_DEVICE_LCD1_INDEX )
+#define ATOM_DEVICE_TV1_SUPPORT                           (0x1L << ATOM_DEVICE_TV1_INDEX  )
+#define ATOM_DEVICE_DFP1_SUPPORT                          (0x1L << ATOM_DEVICE_DFP1_INDEX )
+#define ATOM_DEVICE_CRT2_SUPPORT                          (0x1L << ATOM_DEVICE_CRT2_INDEX )
+#define ATOM_DEVICE_LCD2_SUPPORT                          (0x1L << ATOM_DEVICE_LCD2_INDEX )
+#define ATOM_DEVICE_DFP6_SUPPORT                          (0x1L << ATOM_DEVICE_DFP6_INDEX )
+#define ATOM_DEVICE_DFP2_SUPPORT                          (0x1L << ATOM_DEVICE_DFP2_INDEX )
+#define ATOM_DEVICE_CV_SUPPORT                            (0x1L << ATOM_DEVICE_CV_INDEX   )
+#define ATOM_DEVICE_DFP3_SUPPORT                          (0x1L << ATOM_DEVICE_DFP3_INDEX )
+#define ATOM_DEVICE_DFP4_SUPPORT                          (0x1L << ATOM_DEVICE_DFP4_INDEX )
+#define ATOM_DEVICE_DFP5_SUPPORT                          (0x1L << ATOM_DEVICE_DFP5_INDEX )
+
+#define ATOM_DEVICE_CRT_SUPPORT                           (ATOM_DEVICE_CRT1_SUPPORT | ATOM_DEVICE_CRT2_SUPPORT)
+#define ATOM_DEVICE_DFP_SUPPORT                           (ATOM_DEVICE_DFP1_SUPPORT | ATOM_DEVICE_DFP2_SUPPORT |  ATOM_DEVICE_DFP3_SUPPORT | ATOM_DEVICE_DFP4_SUPPORT | ATOM_DEVICE_DFP5_SUPPORT | ATOM_DEVICE_DFP6_SUPPORT)
+#define ATOM_DEVICE_TV_SUPPORT                            (ATOM_DEVICE_TV1_SUPPORT)
+#define ATOM_DEVICE_LCD_SUPPORT                           (ATOM_DEVICE_LCD1_SUPPORT | ATOM_DEVICE_LCD2_SUPPORT)
 
 #define ATOM_DEVICE_CONNECTOR_TYPE_MASK                   0x000000F0
 #define ATOM_DEVICE_CONNECTOR_TYPE_SHIFT                  0x00000004
@@ -1942,6 +2412,7 @@ usMinDownStreamHTLinkWidth:  same as above.
 #define ATOM_DEVICE_CONNECTOR_CASE_1                      0x0000000E
 #define ATOM_DEVICE_CONNECTOR_DISPLAYPORT                 0x0000000F
 
+
 #define ATOM_DEVICE_DAC_INFO_MASK                         0x0000000F
 #define ATOM_DEVICE_DAC_INFO_SHIFT                        0x00000000
 #define ATOM_DEVICE_DAC_INFO_NODAC                        0x00000000
@@ -1958,139 +2429,150 @@ usMinDownStreamHTLinkWidth:  same as above.
 #define ATOM_DEVICE_I2C_ID_SHIFT                          0x00000004
 #define ATOM_DEVICE_I2C_ID_IS_FOR_NON_MM_USE              0x00000001
 #define ATOM_DEVICE_I2C_ID_IS_FOR_MM_USE                  0x00000002
-#define ATOM_DEVICE_I2C_ID_IS_FOR_SDVO_USE                0x00000003   /* For IGP RS600 */
-#define ATOM_DEVICE_I2C_ID_IS_FOR_DAC_SCL                 0x00000004   /* For IGP RS690 */
+#define ATOM_DEVICE_I2C_ID_IS_FOR_SDVO_USE                0x00000003    //For IGP RS600
+#define ATOM_DEVICE_I2C_ID_IS_FOR_DAC_SCL                 0x00000004    //For IGP RS690
 
 #define ATOM_DEVICE_I2C_HARDWARE_CAP_MASK                 0x00000080
 #define ATOM_DEVICE_I2C_HARDWARE_CAP_SHIFT                0x00000007
 #define        ATOM_DEVICE_USES_SOFTWARE_ASSISTED_I2C            0x00000000
 #define        ATOM_DEVICE_USES_HARDWARE_ASSISTED_I2C            0x00000001
 
-/*   usDeviceSupport: */
-/*   Bits0       = 0 - no CRT1 support= 1- CRT1 is supported */
-/*   Bit 1       = 0 - no LCD1 support= 1- LCD1 is supported */
-/*   Bit 2       = 0 - no TV1  support= 1- TV1  is supported */
-/*   Bit 3       = 0 - no DFP1 support= 1- DFP1 is supported */
-/*   Bit 4       = 0 - no CRT2 support= 1- CRT2 is supported */
-/*   Bit 5       = 0 - no LCD2 support= 1- LCD2 is supported */
-/*   Bit 6       = 0 - no TV2  support= 1- TV2  is supported */
-/*   Bit 7       = 0 - no DFP2 support= 1- DFP2 is supported */
-/*   Bit 8       = 0 - no CV   support= 1- CV   is supported */
-/*   Bit 9       = 0 - no DFP3 support= 1- DFP3 is supported */
-/*   Byte1 (Supported Device Info) */
-/*   Bit 0       = = 0 - no CV support= 1- CV is supported */
-/*  */
-/*  */
-
-/*               ucI2C_ConfigID */
-/*     [7:0] - I2C LINE Associate ID */
-/*           = 0   - no I2C */
-/*     [7]               -       HW_Cap        = 1,  [6:0]=HW assisted I2C ID(HW line selection) */
-/*                           =   0,  [6:0]=SW assisted I2C ID */
-/*     [6-4]     - HW_ENGINE_ID  =       1,  HW engine for NON multimedia use */
-/*                           =   2,      HW engine for Multimedia use */
-/*                           =   3-7     Reserved for future I2C engines */
-/*               [3-0] - I2C_LINE_MUX  = A Mux number when it's HW assisted I2C or GPIO ID when it's SW I2C */
-
-typedef struct _ATOM_I2C_ID_CONFIG {
-#if ATOM_BIG_ENDIAN
-       UCHAR bfHW_Capable:1;
-       UCHAR bfHW_EngineID:3;
-       UCHAR bfI2C_LineMux:4;
-#else
-       UCHAR bfI2C_LineMux:4;
-       UCHAR bfHW_EngineID:3;
-       UCHAR bfHW_Capable:1;
-#endif
-} ATOM_I2C_ID_CONFIG;
-
-typedef union _ATOM_I2C_ID_CONFIG_ACCESS {
-       ATOM_I2C_ID_CONFIG sbfAccess;
-       UCHAR ucAccess;
-} ATOM_I2C_ID_CONFIG_ACCESS;
+//  usDeviceSupport:
+//  Bits0      = 0 - no CRT1 support= 1- CRT1 is supported
+//  Bit 1      = 0 - no LCD1 support= 1- LCD1 is supported
+//  Bit 2      = 0 - no TV1  support= 1- TV1  is supported
+//  Bit 3      = 0 - no DFP1 support= 1- DFP1 is supported
+//  Bit 4      = 0 - no CRT2 support= 1- CRT2 is supported
+//  Bit 5      = 0 - no LCD2 support= 1- LCD2 is supported
+//  Bit 6      = 0 - no DFP6 support= 1- DFP6 is supported
+//  Bit 7      = 0 - no DFP2 support= 1- DFP2 is supported
+//  Bit 8      = 0 - no CV   support= 1- CV   is supported
+//  Bit 9      = 0 - no DFP3 support= 1- DFP3 is supported
+//  Bit 10      = 0 - no DFP4 support= 1- DFP4 is supported
+//  Bit 11      = 0 - no DFP5 support= 1- DFP5 is supported
+//   
+//  
 
 /****************************************************************************/
-/*  Structure used in GPIO_I2C_InfoTable */
+/* Structure used in MclkSS_InfoTable                                       */
 /****************************************************************************/
-typedef struct _ATOM_GPIO_I2C_ASSIGMENT {
-       USHORT usClkMaskRegisterIndex;
-       USHORT usClkEnRegisterIndex;
-       USHORT usClkY_RegisterIndex;
-       USHORT usClkA_RegisterIndex;
-       USHORT usDataMaskRegisterIndex;
-       USHORT usDataEnRegisterIndex;
-       USHORT usDataY_RegisterIndex;
-       USHORT usDataA_RegisterIndex;
-       ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;
-       UCHAR ucClkMaskShift;
-       UCHAR ucClkEnShift;
-       UCHAR ucClkY_Shift;
-       UCHAR ucClkA_Shift;
-       UCHAR ucDataMaskShift;
-       UCHAR ucDataEnShift;
-       UCHAR ucDataY_Shift;
-       UCHAR ucDataA_Shift;
-       UCHAR ucReserved1;
-       UCHAR ucReserved2;
-} ATOM_GPIO_I2C_ASSIGMENT;
-
-typedef struct _ATOM_GPIO_I2C_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_GPIO_I2C_ASSIGMENT asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE];
-} ATOM_GPIO_I2C_INFO;
+//             ucI2C_ConfigID
+//    [7:0] - I2C LINE Associate ID
+//          = 0   - no I2C
+//    [7]              -       HW_Cap        = 1,  [6:0]=HW assisted I2C ID(HW line selection)
+//                          =  0,  [6:0]=SW assisted I2C ID
+//    [6-4]    - HW_ENGINE_ID  =       1,  HW engine for NON multimedia use
+//                          =  2,      HW engine for Multimedia use
+//                          =  3-7     Reserved for future I2C engines
+//             [3-0] - I2C_LINE_MUX  = A Mux number when it's HW assisted I2C or GPIO ID when it's SW I2C
+
+typedef struct _ATOM_I2C_ID_CONFIG
+{
+#if ATOM_BIG_ENDIAN
+  UCHAR   bfHW_Capable:1;
+  UCHAR   bfHW_EngineID:3;
+  UCHAR   bfI2C_LineMux:4;
+#else
+  UCHAR   bfI2C_LineMux:4;
+  UCHAR   bfHW_EngineID:3;
+  UCHAR   bfHW_Capable:1;
+#endif
+}ATOM_I2C_ID_CONFIG;
 
-/****************************************************************************/
-/*  Common Structure used in other structures */
-/****************************************************************************/
+typedef union _ATOM_I2C_ID_CONFIG_ACCESS
+{
+  ATOM_I2C_ID_CONFIG sbfAccess;
+  UCHAR              ucAccess;
+}ATOM_I2C_ID_CONFIG_ACCESS;
+   
+
+/****************************************************************************/ 
+// Structure used in GPIO_I2C_InfoTable
+/****************************************************************************/ 
+typedef struct _ATOM_GPIO_I2C_ASSIGMENT
+{
+  USHORT                    usClkMaskRegisterIndex;
+  USHORT                    usClkEnRegisterIndex;
+  USHORT                    usClkY_RegisterIndex;
+  USHORT                    usClkA_RegisterIndex;
+  USHORT                    usDataMaskRegisterIndex;
+  USHORT                    usDataEnRegisterIndex;
+  USHORT                    usDataY_RegisterIndex;
+  USHORT                    usDataA_RegisterIndex;
+  ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;
+  UCHAR                     ucClkMaskShift;
+  UCHAR                     ucClkEnShift;
+  UCHAR                     ucClkY_Shift;
+  UCHAR                     ucClkA_Shift;
+  UCHAR                     ucDataMaskShift;
+  UCHAR                     ucDataEnShift;
+  UCHAR                     ucDataY_Shift;
+  UCHAR                     ucDataA_Shift;
+  UCHAR                     ucReserved1;
+  UCHAR                     ucReserved2;
+}ATOM_GPIO_I2C_ASSIGMENT;
+
+typedef struct _ATOM_GPIO_I2C_INFO
+{ 
+  ATOM_COMMON_TABLE_HEADER     sHeader;
+  ATOM_GPIO_I2C_ASSIGMENT   asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE];
+}ATOM_GPIO_I2C_INFO;
+
+/****************************************************************************/ 
+// Common Structure used in other structures
+/****************************************************************************/ 
 
 #ifndef _H2INC
-
-/* Please don't add or expand this bitfield structure below, this one will retire soon.! */
-typedef struct _ATOM_MODE_MISC_INFO {
+  
+//Please don't add or expand this bitfield structure below, this one will retire soon.!
+typedef struct _ATOM_MODE_MISC_INFO
+{ 
 #if ATOM_BIG_ENDIAN
-       USHORT Reserved:6;
-       USHORT RGB888:1;
-       USHORT DoubleClock:1;
-       USHORT Interlace:1;
-       USHORT CompositeSync:1;
-       USHORT V_ReplicationBy2:1;
-       USHORT H_ReplicationBy2:1;
-       USHORT VerticalCutOff:1;
-       USHORT VSyncPolarity:1; /* 0=Active High, 1=Active Low */
-       USHORT HSyncPolarity:1; /* 0=Active High, 1=Active Low */
-       USHORT HorizontalCutOff:1;
+  USHORT Reserved:6;
+  USHORT RGB888:1;
+  USHORT DoubleClock:1;
+  USHORT Interlace:1;
+  USHORT CompositeSync:1;
+  USHORT V_ReplicationBy2:1;
+  USHORT H_ReplicationBy2:1;
+  USHORT VerticalCutOff:1;
+  USHORT VSyncPolarity:1;      //0=Active High, 1=Active Low
+  USHORT HSyncPolarity:1;      //0=Active High, 1=Active Low
+  USHORT HorizontalCutOff:1;
 #else
-       USHORT HorizontalCutOff:1;
-       USHORT HSyncPolarity:1; /* 0=Active High, 1=Active Low */
-       USHORT VSyncPolarity:1; /* 0=Active High, 1=Active Low */
-       USHORT VerticalCutOff:1;
-       USHORT H_ReplicationBy2:1;
-       USHORT V_ReplicationBy2:1;
-       USHORT CompositeSync:1;
-       USHORT Interlace:1;
-       USHORT DoubleClock:1;
-       USHORT RGB888:1;
-       USHORT Reserved:6;
+  USHORT HorizontalCutOff:1;
+  USHORT HSyncPolarity:1;      //0=Active High, 1=Active Low
+  USHORT VSyncPolarity:1;      //0=Active High, 1=Active Low
+  USHORT VerticalCutOff:1;
+  USHORT H_ReplicationBy2:1;
+  USHORT V_ReplicationBy2:1;
+  USHORT CompositeSync:1;
+  USHORT Interlace:1;
+  USHORT DoubleClock:1;
+  USHORT RGB888:1;
+  USHORT Reserved:6;           
 #endif
-} ATOM_MODE_MISC_INFO;
-
-typedef union _ATOM_MODE_MISC_INFO_ACCESS {
-       ATOM_MODE_MISC_INFO sbfAccess;
-       USHORT usAccess;
-} ATOM_MODE_MISC_INFO_ACCESS;
-
+}ATOM_MODE_MISC_INFO;
+  
+typedef union _ATOM_MODE_MISC_INFO_ACCESS
+{ 
+  ATOM_MODE_MISC_INFO sbfAccess;
+  USHORT              usAccess;
+}ATOM_MODE_MISC_INFO_ACCESS;
+  
 #else
-
-typedef union _ATOM_MODE_MISC_INFO_ACCESS {
-       USHORT usAccess;
-} ATOM_MODE_MISC_INFO_ACCESS;
-
+  
+typedef union _ATOM_MODE_MISC_INFO_ACCESS
+{ 
+  USHORT              usAccess;
+}ATOM_MODE_MISC_INFO_ACCESS;
+   
 #endif
 
-/*  usModeMiscInfo- */
+// usModeMiscInfo-
 #define ATOM_H_CUTOFF           0x01
-#define ATOM_HSYNC_POLARITY     0x02   /* 0=Active High, 1=Active Low */
-#define ATOM_VSYNC_POLARITY     0x04   /* 0=Active High, 1=Active Low */
+#define ATOM_HSYNC_POLARITY     0x02             //0=Active High, 1=Active Low
+#define ATOM_VSYNC_POLARITY     0x04             //0=Active High, 1=Active Low
 #define ATOM_V_CUTOFF           0x08
 #define ATOM_H_REPLICATIONBY2   0x10
 #define ATOM_V_REPLICATIONBY2   0x20
@@ -2099,10 +2581,10 @@ typedef union _ATOM_MODE_MISC_INFO_ACCESS {
 #define ATOM_DOUBLE_CLOCK_MODE  0x100
 #define ATOM_RGB888_MODE        0x200
 
-/* usRefreshRate- */
+//usRefreshRate-
 #define ATOM_REFRESH_43         43
 #define ATOM_REFRESH_47         47
-#define ATOM_REFRESH_56         56
+#define ATOM_REFRESH_56         56     
 #define ATOM_REFRESH_60         60
 #define ATOM_REFRESH_65         65
 #define ATOM_REFRESH_70         70
@@ -2110,192 +2592,233 @@ typedef union _ATOM_MODE_MISC_INFO_ACCESS {
 #define ATOM_REFRESH_75         75
 #define ATOM_REFRESH_85         85
 
-/*  ATOM_MODE_TIMING data are exactly the same as VESA timing data. */
-/*  Translation from EDID to ATOM_MODE_TIMING, use the following formula. */
-/*  */
-/*       VESA_HTOTAL                     =       VESA_ACTIVE + 2* VESA_BORDER + VESA_BLANK */
-/*                                               =       EDID_HA + EDID_HBL */
-/*       VESA_HDISP                      =       VESA_ACTIVE     =       EDID_HA */
-/*       VESA_HSYNC_START        =       VESA_ACTIVE + VESA_BORDER + VESA_FRONT_PORCH */
-/*                                               =       EDID_HA + EDID_HSO */
-/*       VESA_HSYNC_WIDTH        =       VESA_HSYNC_TIME =       EDID_HSPW */
-/*       VESA_BORDER                     =       EDID_BORDER */
-
-/****************************************************************************/
-/*  Structure used in SetCRTC_UsingDTDTimingTable */
-/****************************************************************************/
-typedef struct _SET_CRTC_USING_DTD_TIMING_PARAMETERS {
-       USHORT usH_Size;
-       USHORT usH_Blanking_Time;
-       USHORT usV_Size;
-       USHORT usV_Blanking_Time;
-       USHORT usH_SyncOffset;
-       USHORT usH_SyncWidth;
-       USHORT usV_SyncOffset;
-       USHORT usV_SyncWidth;
-       ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo;
-       UCHAR ucH_Border;       /*  From DFP EDID */
-       UCHAR ucV_Border;
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucPadding[3];
-} SET_CRTC_USING_DTD_TIMING_PARAMETERS;
-
-/****************************************************************************/
-/*  Structure used in SetCRTC_TimingTable */
-/****************************************************************************/
-typedef struct _SET_CRTC_TIMING_PARAMETERS {
-       USHORT usH_Total;       /*  horizontal total */
-       USHORT usH_Disp;        /*  horizontal display */
-       USHORT usH_SyncStart;   /*  horozontal Sync start */
-       USHORT usH_SyncWidth;   /*  horizontal Sync width */
-       USHORT usV_Total;       /*  vertical total */
-       USHORT usV_Disp;        /*  vertical display */
-       USHORT usV_SyncStart;   /*  vertical Sync start */
-       USHORT usV_SyncWidth;   /*  vertical Sync width */
-       ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo;
-       UCHAR ucCRTC;           /*  ATOM_CRTC1 or ATOM_CRTC2 */
-       UCHAR ucOverscanRight;  /*  right */
-       UCHAR ucOverscanLeft;   /*  left */
-       UCHAR ucOverscanBottom; /*  bottom */
-       UCHAR ucOverscanTop;    /*  top */
-       UCHAR ucReserved;
-} SET_CRTC_TIMING_PARAMETERS;
+// ATOM_MODE_TIMING data are exactly the same as VESA timing data.
+// Translation from EDID to ATOM_MODE_TIMING, use the following formula.
+//
+//     VESA_HTOTAL                     =       VESA_ACTIVE + 2* VESA_BORDER + VESA_BLANK
+//                                             =       EDID_HA + EDID_HBL
+//     VESA_HDISP                      =       VESA_ACTIVE     =       EDID_HA
+//     VESA_HSYNC_START        =       VESA_ACTIVE + VESA_BORDER + VESA_FRONT_PORCH
+//                                             =       EDID_HA + EDID_HSO
+//     VESA_HSYNC_WIDTH        =       VESA_HSYNC_TIME =       EDID_HSPW
+//     VESA_BORDER                     =       EDID_BORDER
+
+/****************************************************************************/ 
+// Structure used in SetCRTC_UsingDTDTimingTable
+/****************************************************************************/ 
+typedef struct _SET_CRTC_USING_DTD_TIMING_PARAMETERS
+{
+  USHORT  usH_Size;
+  USHORT  usH_Blanking_Time;
+  USHORT  usV_Size;
+  USHORT  usV_Blanking_Time;                   
+  USHORT  usH_SyncOffset;
+  USHORT  usH_SyncWidth;
+  USHORT  usV_SyncOffset;
+  USHORT  usV_SyncWidth;
+  ATOM_MODE_MISC_INFO_ACCESS  susModeMiscInfo;  
+  UCHAR   ucH_Border;         // From DFP EDID
+  UCHAR   ucV_Border;
+  UCHAR   ucCRTC;             // ATOM_CRTC1 or ATOM_CRTC2  
+  UCHAR   ucPadding[3];
+}SET_CRTC_USING_DTD_TIMING_PARAMETERS;
+
+/****************************************************************************/ 
+// Structure used in SetCRTC_TimingTable
+/****************************************************************************/ 
+typedef struct _SET_CRTC_TIMING_PARAMETERS
+{
+  USHORT                      usH_Total;        // horizontal total
+  USHORT                      usH_Disp;         // horizontal display
+  USHORT                      usH_SyncStart;    // horozontal Sync start
+  USHORT                      usH_SyncWidth;    // horizontal Sync width
+  USHORT                      usV_Total;        // vertical total
+  USHORT                      usV_Disp;         // vertical display
+  USHORT                      usV_SyncStart;    // vertical Sync start
+  USHORT                      usV_SyncWidth;    // vertical Sync width
+  ATOM_MODE_MISC_INFO_ACCESS  susModeMiscInfo;
+  UCHAR                       ucCRTC;           // ATOM_CRTC1 or ATOM_CRTC2
+  UCHAR                       ucOverscanRight;  // right
+  UCHAR                       ucOverscanLeft;   // left
+  UCHAR                       ucOverscanBottom; // bottom
+  UCHAR                       ucOverscanTop;    // top
+  UCHAR                       ucReserved;
+}SET_CRTC_TIMING_PARAMETERS;
 #define SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION SET_CRTC_TIMING_PARAMETERS
 
-/****************************************************************************/
-/*  Structure used in StandardVESA_TimingTable */
-/*                    AnalogTV_InfoTable */
-/*                    ComponentVideoInfoTable */
-/****************************************************************************/
-typedef struct _ATOM_MODE_TIMING {
-       USHORT usCRTC_H_Total;
-       USHORT usCRTC_H_Disp;
-       USHORT usCRTC_H_SyncStart;
-       USHORT usCRTC_H_SyncWidth;
-       USHORT usCRTC_V_Total;
-       USHORT usCRTC_V_Disp;
-       USHORT usCRTC_V_SyncStart;
-       USHORT usCRTC_V_SyncWidth;
-       USHORT usPixelClock;    /* in 10Khz unit */
-       ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo;
-       USHORT usCRTC_OverscanRight;
-       USHORT usCRTC_OverscanLeft;
-       USHORT usCRTC_OverscanBottom;
-       USHORT usCRTC_OverscanTop;
-       USHORT usReserve;
-       UCHAR ucInternalModeNumber;
-       UCHAR ucRefreshRate;
-} ATOM_MODE_TIMING;
-
-typedef struct _ATOM_DTD_FORMAT {
-       USHORT usPixClk;
-       USHORT usHActive;
-       USHORT usHBlanking_Time;
-       USHORT usVActive;
-       USHORT usVBlanking_Time;
-       USHORT usHSyncOffset;
-       USHORT usHSyncWidth;
-       USHORT usVSyncOffset;
-       USHORT usVSyncWidth;
-       USHORT usImageHSize;
-       USHORT usImageVSize;
-       UCHAR ucHBorder;
-       UCHAR ucVBorder;
-       ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo;
-       UCHAR ucInternalModeNumber;
-       UCHAR ucRefreshRate;
-} ATOM_DTD_FORMAT;
-
-/****************************************************************************/
-/*  Structure used in LVDS_InfoTable */
-/*   * Need a document to describe this table */
-/****************************************************************************/
+/****************************************************************************/ 
+// Structure used in StandardVESA_TimingTable
+//                   AnalogTV_InfoTable 
+//                   ComponentVideoInfoTable
+/****************************************************************************/ 
+typedef struct _ATOM_MODE_TIMING
+{
+  USHORT  usCRTC_H_Total;
+  USHORT  usCRTC_H_Disp;
+  USHORT  usCRTC_H_SyncStart;
+  USHORT  usCRTC_H_SyncWidth;
+  USHORT  usCRTC_V_Total;
+  USHORT  usCRTC_V_Disp;
+  USHORT  usCRTC_V_SyncStart;
+  USHORT  usCRTC_V_SyncWidth;
+  USHORT  usPixelClock;                                                         //in 10Khz unit
+  ATOM_MODE_MISC_INFO_ACCESS  susModeMiscInfo;
+  USHORT  usCRTC_OverscanRight;
+  USHORT  usCRTC_OverscanLeft;
+  USHORT  usCRTC_OverscanBottom;
+  USHORT  usCRTC_OverscanTop;
+  USHORT  usReserve;
+  UCHAR   ucInternalModeNumber;
+  UCHAR   ucRefreshRate;
+}ATOM_MODE_TIMING;
+
+typedef struct _ATOM_DTD_FORMAT
+{
+  USHORT  usPixClk;
+  USHORT  usHActive;
+  USHORT  usHBlanking_Time;
+  USHORT  usVActive;
+  USHORT  usVBlanking_Time;                    
+  USHORT  usHSyncOffset;
+  USHORT  usHSyncWidth;
+  USHORT  usVSyncOffset;
+  USHORT  usVSyncWidth;
+  USHORT  usImageHSize;
+  USHORT  usImageVSize;
+  UCHAR   ucHBorder;
+  UCHAR   ucVBorder;
+  ATOM_MODE_MISC_INFO_ACCESS susModeMiscInfo;
+  UCHAR   ucInternalModeNumber;
+  UCHAR   ucRefreshRate;
+}ATOM_DTD_FORMAT;
+
+/****************************************************************************/ 
+// Structure used in LVDS_InfoTable 
+//  * Need a document to describe this table
+/****************************************************************************/ 
 #define SUPPORTED_LCD_REFRESHRATE_30Hz          0x0004
 #define SUPPORTED_LCD_REFRESHRATE_40Hz          0x0008
 #define SUPPORTED_LCD_REFRESHRATE_50Hz          0x0010
 #define SUPPORTED_LCD_REFRESHRATE_60Hz          0x0020
 
-/* Once DAL sees this CAP is set, it will read EDID from LCD on its own instead of using sLCDTiming in ATOM_LVDS_INFO_V12. */
-/* Other entries in ATOM_LVDS_INFO_V12 are still valid/useful to DAL */
-#define        LCDPANEL_CAP_READ_EDID                                                                  0x1
-
-/* ucTableFormatRevision=1 */
-/* ucTableContentRevision=1 */
-typedef struct _ATOM_LVDS_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_DTD_FORMAT sLCDTiming;
-       USHORT usModePatchTableOffset;
-       USHORT usSupportedRefreshRate;  /* Refer to panel info table in ATOMBIOS extension Spec. */
-       USHORT usOffDelayInMs;
-       UCHAR ucPowerSequenceDigOntoDEin10Ms;
-       UCHAR ucPowerSequenceDEtoBLOnin10Ms;
-       UCHAR ucLVDS_Misc;      /*  Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} */
-       /*  Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} */
-       /*  Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} */
-       /*  Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} */
-       UCHAR ucPanelDefaultRefreshRate;
-       UCHAR ucPanelIdentification;
-       UCHAR ucSS_Id;
-} ATOM_LVDS_INFO;
-
-/* ucTableFormatRevision=1 */
-/* ucTableContentRevision=2 */
-typedef struct _ATOM_LVDS_INFO_V12 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_DTD_FORMAT sLCDTiming;
-       USHORT usExtInfoTableOffset;
-       USHORT usSupportedRefreshRate;  /* Refer to panel info table in ATOMBIOS extension Spec. */
-       USHORT usOffDelayInMs;
-       UCHAR ucPowerSequenceDigOntoDEin10Ms;
-       UCHAR ucPowerSequenceDEtoBLOnin10Ms;
-       UCHAR ucLVDS_Misc;      /*  Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level} */
-       /*  Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888} */
-       /*  Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled} */
-       /*  Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled} */
-       UCHAR ucPanelDefaultRefreshRate;
-       UCHAR ucPanelIdentification;
-       UCHAR ucSS_Id;
-       USHORT usLCDVenderID;
-       USHORT usLCDProductID;
-       UCHAR ucLCDPanel_SpecialHandlingCap;
-       UCHAR ucPanelInfoSize;  /*   start from ATOM_DTD_FORMAT to end of panel info, include ExtInfoTable */
-       UCHAR ucReserved[2];
-} ATOM_LVDS_INFO_V12;
+//ucTableFormatRevision=1
+//ucTableContentRevision=1
+typedef struct _ATOM_LVDS_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ATOM_DTD_FORMAT     sLCDTiming;
+  USHORT              usModePatchTableOffset;
+  USHORT              usSupportedRefreshRate;     //Refer to panel info table in ATOMBIOS extension Spec.
+  USHORT              usOffDelayInMs;
+  UCHAR               ucPowerSequenceDigOntoDEin10Ms;
+  UCHAR               ucPowerSequenceDEtoBLOnin10Ms;
+  UCHAR               ucLVDS_Misc;               // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level}
+                                                 // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888}
+                                                 // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled}
+                                                 // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled}
+  UCHAR               ucPanelDefaultRefreshRate;
+  UCHAR               ucPanelIdentification;
+  UCHAR               ucSS_Id;
+}ATOM_LVDS_INFO;
+
+//ucTableFormatRevision=1
+//ucTableContentRevision=2
+typedef struct _ATOM_LVDS_INFO_V12
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ATOM_DTD_FORMAT     sLCDTiming;
+  USHORT              usExtInfoTableOffset;
+  USHORT              usSupportedRefreshRate;     //Refer to panel info table in ATOMBIOS extension Spec.
+  USHORT              usOffDelayInMs;
+  UCHAR               ucPowerSequenceDigOntoDEin10Ms;
+  UCHAR               ucPowerSequenceDEtoBLOnin10Ms;
+  UCHAR               ucLVDS_Misc;               // Bit0:{=0:single, =1:dual},Bit1 {=0:666RGB, =1:888RGB},Bit2:3:{Grey level}
+                                                 // Bit4:{=0:LDI format for RGB888, =1 FPDI format for RGB888}
+                                                 // Bit5:{=0:Spatial Dithering disabled;1 Spatial Dithering enabled}
+                                                 // Bit6:{=0:Temporal Dithering disabled;1 Temporal Dithering enabled}
+  UCHAR               ucPanelDefaultRefreshRate;
+  UCHAR               ucPanelIdentification;
+  UCHAR               ucSS_Id;
+  USHORT              usLCDVenderID;
+  USHORT              usLCDProductID;
+  UCHAR               ucLCDPanel_SpecialHandlingCap; 
+       UCHAR                                                           ucPanelInfoSize;                                        //  start from ATOM_DTD_FORMAT to end of panel info, include ExtInfoTable
+  UCHAR               ucReserved[2];
+}ATOM_LVDS_INFO_V12;
+
+//Definitions for ucLCDPanel_SpecialHandlingCap:
+
+//Once DAL sees this CAP is set, it will read EDID from LCD on its own instead of using sLCDTiming in ATOM_LVDS_INFO_V12. 
+//Other entries in ATOM_LVDS_INFO_V12 are still valid/useful to DAL 
+#define        LCDPANEL_CAP_READ_EDID                  0x1
+
+//If a design supports DRR (dynamic refresh rate) on internal panels (LVDS or EDP), this cap is set in ucLCDPanel_SpecialHandlingCap together
+//with multiple supported refresh rates@usSupportedRefreshRate. This cap should not be set when only slow refresh rate is supported (static
+//refresh rate switch by SW. This is only valid from ATOM_LVDS_INFO_V12
+#define        LCDPANEL_CAP_DRR_SUPPORTED              0x2
+
+//Use this cap bit for a quick reference whether an embadded panel (LCD1 ) is LVDS or eDP.
+#define        LCDPANEL_CAP_eDP                        0x4
+
+
+//Color Bit Depth definition in EDID V1.4 @BYTE 14h
+//Bit 6  5  4
+                              //      0  0  0  -  Color bit depth is undefined
+                              //      0  0  1  -  6 Bits per Primary Color
+                              //      0  1  0  -  8 Bits per Primary Color
+                              //      0  1  1  - 10 Bits per Primary Color
+                              //      1  0  0  - 12 Bits per Primary Color
+                              //      1  0  1  - 14 Bits per Primary Color
+                              //      1  1  0  - 16 Bits per Primary Color
+                              //      1  1  1  - Reserved
+
+#define PANEL_COLOR_BIT_DEPTH_MASK    0x70
+
+// Bit7:{=0:Random Dithering disabled;1 Random Dithering enabled}   
+#define PANEL_RANDOM_DITHER   0x80
+#define PANEL_RANDOM_DITHER_MASK   0x80
+
 
 #define ATOM_LVDS_INFO_LAST  ATOM_LVDS_INFO_V12
 
-typedef struct _ATOM_PATCH_RECORD_MODE {
-       UCHAR ucRecordType;
-       USHORT usHDisp;
-       USHORT usVDisp;
-} ATOM_PATCH_RECORD_MODE;
+typedef struct  _ATOM_PATCH_RECORD_MODE
+{
+  UCHAR     ucRecordType;
+  USHORT    usHDisp;
+  USHORT    usVDisp;
+}ATOM_PATCH_RECORD_MODE;
 
-typedef struct _ATOM_LCD_RTS_RECORD {
-       UCHAR ucRecordType;
-       UCHAR ucRTSValue;
-} ATOM_LCD_RTS_RECORD;
+typedef struct  _ATOM_LCD_RTS_RECORD
+{
+  UCHAR     ucRecordType;
+  UCHAR     ucRTSValue;
+}ATOM_LCD_RTS_RECORD;
 
-/* !! If the record below exits, it shoud always be the first record for easy use in command table!!! */
-typedef struct _ATOM_LCD_MODE_CONTROL_CAP {
-       UCHAR ucRecordType;
-       USHORT usLCDCap;
-} ATOM_LCD_MODE_CONTROL_CAP;
+//!! If the record below exits, it shoud always be the first record for easy use in command table!!! 
+// The record below is only used when LVDS_Info is present. From ATOM_LVDS_INFO_V12, use ucLCDPanel_SpecialHandlingCap instead.
+typedef struct  _ATOM_LCD_MODE_CONTROL_CAP
+{
+  UCHAR     ucRecordType;
+  USHORT    usLCDCap;
+}ATOM_LCD_MODE_CONTROL_CAP;
 
 #define LCD_MODE_CAP_BL_OFF                   1
 #define LCD_MODE_CAP_CRTC_OFF                 2
 #define LCD_MODE_CAP_PANEL_OFF                4
 
-typedef struct _ATOM_FAKE_EDID_PATCH_RECORD {
-       UCHAR ucRecordType;
-       UCHAR ucFakeEDIDLength;
-       UCHAR ucFakeEDIDString[1];      /*  This actually has ucFakeEdidLength elements. */
+typedef struct _ATOM_FAKE_EDID_PATCH_RECORD
+{
+  UCHAR ucRecordType;
+  UCHAR ucFakeEDIDLength;
+  UCHAR ucFakeEDIDString[1];    // This actually has ucFakeEdidLength elements.
 } ATOM_FAKE_EDID_PATCH_RECORD;
 
-typedef struct _ATOM_PANEL_RESOLUTION_PATCH_RECORD {
-       UCHAR ucRecordType;
-       USHORT usHSize;
-       USHORT usVSize;
-} ATOM_PANEL_RESOLUTION_PATCH_RECORD;
+typedef struct  _ATOM_PANEL_RESOLUTION_PATCH_RECORD
+{
+   UCHAR    ucRecordType;
+   USHORT              usHSize;
+   USHORT              usVSize;
+}ATOM_PANEL_RESOLUTION_PATCH_RECORD;
 
 #define LCD_MODE_PATCH_RECORD_MODE_TYPE       1
 #define LCD_RTS_RECORD_TYPE                   2
@@ -2306,21 +2829,25 @@ typedef struct _ATOM_PANEL_RESOLUTION_PATCH_RECORD {
 
 /****************************Spread Spectrum Info Table Definitions **********************/
 
-/* ucTableFormatRevision=1 */
-/* ucTableContentRevision=2 */
-typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT {
-       USHORT usSpreadSpectrumPercentage;
-       UCHAR ucSpreadSpectrumType;     /* Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Others:TBD */
-       UCHAR ucSS_Step;
-       UCHAR ucSS_Delay;
-       UCHAR ucSS_Id;
-       UCHAR ucRecommendedRef_Div;
-       UCHAR ucSS_Range;       /* it was reserved for V11 */
-} ATOM_SPREAD_SPECTRUM_ASSIGNMENT;
+//ucTableFormatRevision=1
+//ucTableContentRevision=2
+typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT
+{
+  USHORT              usSpreadSpectrumPercentage; 
+  UCHAR               ucSpreadSpectrumType;        //Bit1=0 Down Spread,=1 Center Spread. Bit1=1 Ext. =0 Int. Bit2=1: PCIE REFCLK SS =0 iternal PPLL SS  Others:TBD
+  UCHAR               ucSS_Step;
+  UCHAR               ucSS_Delay;
+  UCHAR               ucSS_Id;
+  UCHAR               ucRecommendedRef_Div;
+  UCHAR               ucSS_Range;               //it was reserved for V11
+}ATOM_SPREAD_SPECTRUM_ASSIGNMENT;
 
 #define ATOM_MAX_SS_ENTRY                      16
-#define ATOM_DP_SS_ID1                                                                                          0x0f1  /*  SS modulation freq=30k */
-#define ATOM_DP_SS_ID2                                                                                          0x0f2  /*  SS modulation freq=33k */
+#define ATOM_DP_SS_ID1                                                                                          0x0f1                  // SS ID for internal DP stream at 2.7Ghz. if ATOM_DP_SS_ID2 does not exist in SS_InfoTable, it is used for internal DP stream at 1.62Ghz as well. 
+#define ATOM_DP_SS_ID2                                                                                          0x0f2                  // SS ID for internal DP stream at 1.62Ghz, if it exists in SS_InfoTable. 
+#define ATOM_LVLINK_2700MHz_SS_ID              0x0f3      // SS ID for LV link translator chip at 2.7Ghz
+#define ATOM_LVLINK_1620MHz_SS_ID              0x0f4      // SS ID for LV link translator chip at 1.62Ghz
+
 
 #define ATOM_SS_DOWN_SPREAD_MODE_MASK          0x00000000
 #define ATOM_SS_DOWN_SPREAD_MODE               0x00000000
@@ -2329,29 +2856,30 @@ typedef struct _ATOM_SPREAD_SPECTRUM_ASSIGNMENT {
 #define ATOM_INTERNAL_SS_MASK                  0x00000000
 #define ATOM_EXTERNAL_SS_MASK                  0x00000002
 #define EXEC_SS_STEP_SIZE_SHIFT                2
-#define EXEC_SS_DELAY_SHIFT                    4
+#define EXEC_SS_DELAY_SHIFT                    4    
 #define ACTIVEDATA_TO_BLON_DELAY_SHIFT         4
 
-typedef struct _ATOM_SPREAD_SPECTRUM_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_SPREAD_SPECTRUM_ASSIGNMENT asSS_Info[ATOM_MAX_SS_ENTRY];
-} ATOM_SPREAD_SPECTRUM_INFO;
-
-/****************************************************************************/
-/*  Structure used in AnalogTV_InfoTable (Top level) */
-/****************************************************************************/
-/* ucTVBootUpDefaultStd definiton: */
-
-/* ATOM_TV_NTSC                1 */
-/* ATOM_TV_NTSCJ               2 */
-/* ATOM_TV_PAL                 3 */
-/* ATOM_TV_PALM                4 */
-/* ATOM_TV_PALCN               5 */
-/* ATOM_TV_PALN                6 */
-/* ATOM_TV_PAL60               7 */
-/* ATOM_TV_SECAM               8 */
-
-/* ucTVSuppportedStd definition: */
+typedef struct _ATOM_SPREAD_SPECTRUM_INFO
+{ 
+  ATOM_COMMON_TABLE_HEADER     sHeader;
+  ATOM_SPREAD_SPECTRUM_ASSIGNMENT   asSS_Info[ATOM_MAX_SS_ENTRY];
+}ATOM_SPREAD_SPECTRUM_INFO;
+
+/****************************************************************************/ 
+// Structure used in AnalogTV_InfoTable (Top level)
+/****************************************************************************/ 
+//ucTVBootUpDefaultStd definiton:
+
+//ATOM_TV_NTSC                1
+//ATOM_TV_NTSCJ               2
+//ATOM_TV_PAL                 3
+//ATOM_TV_PALM                4
+//ATOM_TV_PALCN               5
+//ATOM_TV_PALN                6
+//ATOM_TV_PAL60               7
+//ATOM_TV_SECAM               8
+
+//ucTVSupportedStd definition:
 #define NTSC_SUPPORT          0x1
 #define NTSCJ_SUPPORT         0x2
 
@@ -2364,46 +2892,58 @@ typedef struct _ATOM_SPREAD_SPECTRUM_INFO {
 
 #define MAX_SUPPORTED_TV_TIMING    2
 
-typedef struct _ATOM_ANALOG_TV_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucTV_SupportedStandard;
-       UCHAR ucTV_BootUpDefaultStandard;
-       UCHAR ucExt_TV_ASIC_ID;
-       UCHAR ucExt_TV_ASIC_SlaveAddr;
-       /*ATOM_DTD_FORMAT          aModeTimings[MAX_SUPPORTED_TV_TIMING]; */
-       ATOM_MODE_TIMING aModeTimings[MAX_SUPPORTED_TV_TIMING];
-} ATOM_ANALOG_TV_INFO;
+typedef struct _ATOM_ANALOG_TV_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  UCHAR                    ucTV_SupportedStandard;
+  UCHAR                    ucTV_BootUpDefaultStandard; 
+  UCHAR                    ucExt_TV_ASIC_ID;
+  UCHAR                    ucExt_TV_ASIC_SlaveAddr;
+  /*ATOM_DTD_FORMAT          aModeTimings[MAX_SUPPORTED_TV_TIMING];*/
+  ATOM_MODE_TIMING         aModeTimings[MAX_SUPPORTED_TV_TIMING];
+}ATOM_ANALOG_TV_INFO;
 
 #define MAX_SUPPORTED_TV_TIMING_V1_2    3
 
-typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR                    ucTV_SupportedStandard;
-       UCHAR                    ucTV_BootUpDefaultStandard;
-       UCHAR                    ucExt_TV_ASIC_ID;
-       UCHAR                    ucExt_TV_ASIC_SlaveAddr;
-       ATOM_DTD_FORMAT          aModeTimings[MAX_SUPPORTED_TV_TIMING];
-} ATOM_ANALOG_TV_INFO_V1_2;
+typedef struct _ATOM_ANALOG_TV_INFO_V1_2
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  UCHAR                    ucTV_SupportedStandard;
+  UCHAR                    ucTV_BootUpDefaultStandard; 
+  UCHAR                    ucExt_TV_ASIC_ID;
+  UCHAR                    ucExt_TV_ASIC_SlaveAddr;
+  ATOM_DTD_FORMAT          aModeTimings[MAX_SUPPORTED_TV_TIMING];
+}ATOM_ANALOG_TV_INFO_V1_2;
+
+typedef struct _ATOM_DPCD_INFO
+{
+  UCHAR   ucRevisionNumber;        //10h : Revision 1.0; 11h : Revision 1.1   
+  UCHAR   ucMaxLinkRate;           //06h : 1.62Gbps per lane; 0Ah = 2.7Gbps per lane
+  UCHAR   ucMaxLane;               //Bits 4:0 = MAX_LANE_COUNT (1/2/4). Bit 7 = ENHANCED_FRAME_CAP 
+  UCHAR   ucMaxDownSpread;         //Bit0 = 0: No Down spread; Bit0 = 1: 0.5% (Subject to change according to DP spec)
+}ATOM_DPCD_INFO;
+
+#define ATOM_DPCD_MAX_LANE_MASK    0x1F
 
 /**************************************************************************/
-/*  VRAM usage and their definitions */
+// VRAM usage and their defintions
 
-/*  One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. */
-/*  Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. */
-/*  All the addresses below are the offsets from the frame buffer start.They all MUST be Dword aligned! */
-/*  To driver: The physical address of this memory portion=mmFB_START(4K aligned)+ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR */
-/*  To Bios:  ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR->MM_INDEX */
+// One chunk of VRAM used by Bios are for HWICON surfaces,EDID data.
+// Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below.
+// All the addresses below are the offsets from the frame buffer start.They all MUST be Dword aligned!
+// To driver: The physical address of this memory portion=mmFB_START(4K aligned)+ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR
+// To Bios:  ATOMBIOS_VRAM_USAGE_START_ADDR+ATOM_x_ADDR->MM_INDEX 
 
 #ifndef VESA_MEMORY_IN_64K_BLOCK
-#define VESA_MEMORY_IN_64K_BLOCK        0x100  /* 256*64K=16Mb (Max. VESA memory is 16Mb!) */
+#define VESA_MEMORY_IN_64K_BLOCK        0x100       //256*64K=16Mb (Max. VESA memory is 16Mb!)
 #endif
 
-#define ATOM_EDID_RAW_DATASIZE          256    /* In Bytes */
-#define ATOM_HWICON_SURFACE_SIZE        4096   /* In Bytes */
+#define ATOM_EDID_RAW_DATASIZE          256         //In Bytes
+#define ATOM_HWICON_SURFACE_SIZE        4096        //In Bytes
 #define ATOM_HWICON_INFOTABLE_SIZE      32
 #define MAX_DTD_MODE_IN_VRAM            6
-#define ATOM_DTD_MODE_SUPPORT_TBL_SIZE  (MAX_DTD_MODE_IN_VRAM*28)      /* 28= (SIZEOF ATOM_DTD_FORMAT) */
-#define ATOM_STD_MODE_SUPPORT_TBL_SIZE  (32*8) /* 32 is a predefined number,8= (SIZEOF ATOM_STD_FORMAT) */
+#define ATOM_DTD_MODE_SUPPORT_TBL_SIZE  (MAX_DTD_MODE_IN_VRAM*28)    //28= (SIZEOF ATOM_DTD_FORMAT) 
+#define ATOM_STD_MODE_SUPPORT_TBL_SIZE  32*8                         //32 is a predefined number,8= (SIZEOF ATOM_STD_FORMAT)
 #define DFP_ENCODER_TYPE_OFFSET                                        0x80
 #define DP_ENCODER_LANE_NUM_OFFSET                     0x84
 #define DP_ENCODER_LINK_RATE_OFFSET                    0x88
@@ -2417,7 +2957,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
 
 #define ATOM_LCD1_EDID_ADDR             (ATOM_CRT1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
 #define ATOM_LCD1_DTD_MODE_TBL_ADDR     (ATOM_LCD1_EDID_ADDR + ATOM_EDID_RAW_DATASIZE)
-#define ATOM_LCD1_STD_MODE_TBL_ADDR    (ATOM_LCD1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
+#define ATOM_LCD1_STD_MODE_TBL_ADDR    (ATOM_LCD1_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
 
 #define ATOM_TV1_DTD_MODE_TBL_ADDR      (ATOM_LCD1_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
 
@@ -2431,13 +2971,13 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
 
 #define ATOM_LCD2_EDID_ADDR             (ATOM_CRT2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
 #define ATOM_LCD2_DTD_MODE_TBL_ADDR     (ATOM_LCD2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE)
-#define ATOM_LCD2_STD_MODE_TBL_ADDR    (ATOM_LCD2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
+#define ATOM_LCD2_STD_MODE_TBL_ADDR    (ATOM_LCD2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
 
-#define ATOM_TV2_EDID_ADDR              (ATOM_LCD2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
-#define ATOM_TV2_DTD_MODE_TBL_ADDR      (ATOM_TV2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE)
-#define ATOM_TV2_STD_MODE_TBL_ADDR       (ATOM_TV2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
+#define ATOM_DFP6_EDID_ADDR             (ATOM_LCD2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
+#define ATOM_DFP6_DTD_MODE_TBL_ADDR     (ATOM_DFP6_EDID_ADDR + ATOM_EDID_RAW_DATASIZE)
+#define ATOM_DFP6_STD_MODE_TBL_ADDR     (ATOM_DFP6_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
 
-#define ATOM_DFP2_EDID_ADDR             (ATOM_TV2_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
+#define ATOM_DFP2_EDID_ADDR             (ATOM_DFP6_STD_MODE_TBL_ADDR + ATOM_STD_MODE_SUPPORT_TBL_SIZE)
 #define ATOM_DFP2_DTD_MODE_TBL_ADDR     (ATOM_DFP2_EDID_ADDR + ATOM_EDID_RAW_DATASIZE)
 #define ATOM_DFP2_STD_MODE_TBL_ADDR     (ATOM_DFP2_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
 
@@ -2457,533 +2997,850 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
 #define ATOM_DFP5_DTD_MODE_TBL_ADDR     (ATOM_DFP5_EDID_ADDR + ATOM_EDID_RAW_DATASIZE)
 #define ATOM_DFP5_STD_MODE_TBL_ADDR     (ATOM_DFP5_DTD_MODE_TBL_ADDR + ATOM_DTD_MODE_SUPPORT_TBL_SIZE)
 
-#define ATOM_DP_TRAINING_TBL_ADDR      (ATOM_DFP5_STD_MODE_TBL_ADDR+ATOM_STD_MODE_SUPPORT_TBL_SIZE)
+#define ATOM_DP_TRAINING_TBL_ADDR                              (ATOM_DFP5_STD_MODE_TBL_ADDR+ATOM_STD_MODE_SUPPORT_TBL_SIZE)       
 
-#define ATOM_STACK_STORAGE_START        (ATOM_DP_TRAINING_TBL_ADDR + 256)
-#define ATOM_STACK_STORAGE_END          (ATOM_STACK_STORAGE_START + 512)
+#define ATOM_STACK_STORAGE_START        (ATOM_DP_TRAINING_TBL_ADDR+256)       
+#define ATOM_STACK_STORAGE_END          ATOM_STACK_STORAGE_START+512        
 
-/* The size below is in Kb! */
+//The size below is in Kb!
 #define ATOM_VRAM_RESERVE_SIZE         ((((ATOM_STACK_STORAGE_END - ATOM_HWICON1_SURFACE_ADDR)>>10)+4)&0xFFFC)
-
+   
 #define        ATOM_VRAM_OPERATION_FLAGS_MASK         0xC0000000L
 #define ATOM_VRAM_OPERATION_FLAGS_SHIFT        30
 #define        ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION   0x1
 #define        ATOM_VRAM_BLOCK_NEEDS_RESERVATION      0x0
 
-/***********************************************************************************/
-/*  Structure used in VRAM_UsageByFirmwareTable */
-/*  Note1: This table is filled by SetBiosReservationStartInFB in CoreCommSubs.asm */
-/*         at running time. */
-/*  note2: From RV770, the memory is more than 32bit addressable, so we will change */
-/*         ucTableFormatRevision=1,ucTableContentRevision=4, the strcuture remains */
-/*         exactly same as 1.1 and 1.2 (1.3 is never in use), but ulStartAddrUsedByFirmware */
-/*         (in offset to start of memory address) is KB aligned instead of byte aligend. */
-/***********************************************************************************/
+/***********************************************************************************/  
+// Structure used in VRAM_UsageByFirmwareTable
+// Note1: This table is filled by SetBiosReservationStartInFB in CoreCommSubs.asm
+//        at running time.   
+// note2: From RV770, the memory is more than 32bit addressable, so we will change 
+//        ucTableFormatRevision=1,ucTableContentRevision=4, the strcuture remains 
+//        exactly same as 1.1 and 1.2 (1.3 is never in use), but ulStartAddrUsedByFirmware 
+//        (in offset to start of memory address) is KB aligned instead of byte aligend.
+/***********************************************************************************/  
+// Note3:
+/* If we change usReserved to "usFBUsedbyDrvInKB", then to VBIOS this usFBUsedbyDrvInKB is a predefined, unchanged constant across VGA or non VGA adapter,
+for CAIL, The size of FB access area is known, only thing missing is the Offset of FB Access area, so we can  have:
+
+If (ulStartAddrUsedByFirmware!=0)
+FBAccessAreaOffset= ulStartAddrUsedByFirmware - usFBUsedbyDrvInKB;
+Reserved area has been claimed by VBIOS including this FB access area; CAIL doesn't need to reserve any extra area for this purpose
+else   //Non VGA case
+ if (FB_Size<=2Gb)
+    FBAccessAreaOffset= FB_Size - usFBUsedbyDrvInKB;
+ else
+         FBAccessAreaOffset= Aper_Size - usFBUsedbyDrvInKB
+
+CAIL needs to claim an reserved area defined by FBAccessAreaOffset and usFBUsedbyDrvInKB in non VGA case.*/
+
 #define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO                      1
 
-typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO {
-       ULONG ulStartAddrUsedByFirmware;
-       USHORT usFirmwareUseInKb;
-       USHORT usReserved;
-} ATOM_FIRMWARE_VRAM_RESERVE_INFO;
+typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO
+{
+  ULONG   ulStartAddrUsedByFirmware;
+  USHORT  usFirmwareUseInKb;
+  USHORT  usReserved;
+}ATOM_FIRMWARE_VRAM_RESERVE_INFO;
 
-typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_FIRMWARE_VRAM_RESERVE_INFO
-           asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO];
-} ATOM_VRAM_USAGE_BY_FIRMWARE;
+typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ATOM_FIRMWARE_VRAM_RESERVE_INFO      asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO];
+}ATOM_VRAM_USAGE_BY_FIRMWARE;
 
-/****************************************************************************/
-/*  Structure used in GPIO_Pin_LUTTable */
-/****************************************************************************/
-typedef struct _ATOM_GPIO_PIN_ASSIGNMENT {
-       USHORT usGpioPin_AIndex;
-       UCHAR ucGpioPinBitShift;
-       UCHAR ucGPIO_ID;
-} ATOM_GPIO_PIN_ASSIGNMENT;
+// change verion to 1.5, when allow driver to allocate the vram area for command table access. 
+typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO_V1_5
+{
+  ULONG   ulStartAddrUsedByFirmware;
+  USHORT  usFirmwareUseInKb;
+  USHORT  usFBUsedByDrvInKb;
+}ATOM_FIRMWARE_VRAM_RESERVE_INFO_V1_5;
 
-typedef struct _ATOM_GPIO_PIN_LUT {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_GPIO_PIN_ASSIGNMENT asGPIO_Pin[1];
-} ATOM_GPIO_PIN_LUT;
+typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE_V1_5
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ATOM_FIRMWARE_VRAM_RESERVE_INFO_V1_5 asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO];
+}ATOM_VRAM_USAGE_BY_FIRMWARE_V1_5;
+
+/****************************************************************************/ 
+// Structure used in GPIO_Pin_LUTTable
+/****************************************************************************/ 
+typedef struct _ATOM_GPIO_PIN_ASSIGNMENT
+{
+  USHORT                   usGpioPin_AIndex;
+  UCHAR                    ucGpioPinBitShift;
+  UCHAR                    ucGPIO_ID;
+}ATOM_GPIO_PIN_ASSIGNMENT;
 
-/****************************************************************************/
-/*  Structure used in ComponentVideoInfoTable */
-/****************************************************************************/
+typedef struct _ATOM_GPIO_PIN_LUT
+{
+  ATOM_COMMON_TABLE_HEADER  sHeader;
+  ATOM_GPIO_PIN_ASSIGNMENT     asGPIO_Pin[1];
+}ATOM_GPIO_PIN_LUT;
+
+/****************************************************************************/ 
+// Structure used in ComponentVideoInfoTable   
+/****************************************************************************/ 
 #define GPIO_PIN_ACTIVE_HIGH          0x1
 
 #define MAX_SUPPORTED_CV_STANDARDS    5
 
-/*  definitions for ATOM_D_INFO.ucSettings */
-#define ATOM_GPIO_SETTINGS_BITSHIFT_MASK  0x1F /*  [4:0] */
-#define ATOM_GPIO_SETTINGS_RESERVED_MASK  0x60 /*  [6:5] = must be zeroed out */
-#define ATOM_GPIO_SETTINGS_ACTIVE_MASK    0x80 /*  [7] */
+// definitions for ATOM_D_INFO.ucSettings
+#define ATOM_GPIO_SETTINGS_BITSHIFT_MASK  0x1F    // [4:0]
+#define ATOM_GPIO_SETTINGS_RESERVED_MASK  0x60    // [6:5] = must be zeroed out
+#define ATOM_GPIO_SETTINGS_ACTIVE_MASK    0x80    // [7]
 
-typedef struct _ATOM_GPIO_INFO {
-       USHORT usAOffset;
-       UCHAR ucSettings;
-       UCHAR ucReserved;
-} ATOM_GPIO_INFO;
+typedef struct _ATOM_GPIO_INFO
+{
+  USHORT  usAOffset;
+  UCHAR   ucSettings;
+  UCHAR   ucReserved;
+}ATOM_GPIO_INFO;
 
-/*  definitions for ATOM_COMPONENT_VIDEO_INFO.ucMiscInfo (bit vector) */
+// definitions for ATOM_COMPONENT_VIDEO_INFO.ucMiscInfo (bit vector)
 #define ATOM_CV_RESTRICT_FORMAT_SELECTION           0x2
 
-/*  definitions for ATOM_COMPONENT_VIDEO_INFO.uc480i/uc480p/uc720p/uc1080i */
-#define ATOM_GPIO_DEFAULT_MODE_EN                   0x80       /* [7]; */
-#define ATOM_GPIO_SETTING_PERMODE_MASK              0x7F       /* [6:0] */
-
-/*  definitions for ATOM_COMPONENT_VIDEO_INFO.ucLetterBoxMode */
-/* Line 3 out put 5V. */
-#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_A       0x01       /* represent gpio 3 state for 16:9 */
-#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_B       0x02       /* represent gpio 4 state for 16:9 */
-#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_SHIFT   0x0
-
-/* Line 3 out put 2.2V */
-#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_A 0x04       /* represent gpio 3 state for 4:3 Letter box */
-#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_B 0x08       /* represent gpio 4 state for 4:3 Letter box */
-#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_SHIFT 0x2
-
-/* Line 3 out put 0V */
-#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_A        0x10       /* represent gpio 3 state for 4:3 */
-#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_B        0x20       /* represent gpio 4 state for 4:3 */
-#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_SHIFT    0x4
-
-#define ATOM_CV_LINE3_ASPECTRATIO_MASK              0x3F       /*  bit [5:0] */
-
-#define ATOM_CV_LINE3_ASPECTRATIO_EXIST             0x80       /* bit 7 */
-
-/* GPIO bit index in gpio setting per mode value, also represend the block no. in gpio blocks. */
-#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_A   3    /* bit 3 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. */
-#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_B   4    /* bit 4 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode. */
-
-typedef struct _ATOM_COMPONENT_VIDEO_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usMask_PinRegisterIndex;
-       USHORT usEN_PinRegisterIndex;
-       USHORT usY_PinRegisterIndex;
-       USHORT usA_PinRegisterIndex;
-       UCHAR ucBitShift;
-       UCHAR ucPinActiveState; /* ucPinActiveState: Bit0=1 active high, =0 active low */
-       ATOM_DTD_FORMAT sReserved;      /*  must be zeroed out */
-       UCHAR ucMiscInfo;
-       UCHAR uc480i;
-       UCHAR uc480p;
-       UCHAR uc720p;
-       UCHAR uc1080i;
-       UCHAR ucLetterBoxMode;
-       UCHAR ucReserved[3];
-       UCHAR ucNumOfWbGpioBlocks;      /* For Component video D-Connector support. If zere, NTSC type connector */
-       ATOM_GPIO_INFO aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS];
-       ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_CV_STANDARDS];
-} ATOM_COMPONENT_VIDEO_INFO;
-
-/* ucTableFormatRevision=2 */
-/* ucTableContentRevision=1 */
-typedef struct _ATOM_COMPONENT_VIDEO_INFO_V21 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucMiscInfo;
-       UCHAR uc480i;
-       UCHAR uc480p;
-       UCHAR uc720p;
-       UCHAR uc1080i;
-       UCHAR ucReserved;
-       UCHAR ucLetterBoxMode;
-       UCHAR ucNumOfWbGpioBlocks;      /* For Component video D-Connector support. If zere, NTSC type connector */
-       ATOM_GPIO_INFO aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS];
-       ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_CV_STANDARDS];
-} ATOM_COMPONENT_VIDEO_INFO_V21;
+// definitions for ATOM_COMPONENT_VIDEO_INFO.uc480i/uc480p/uc720p/uc1080i
+#define ATOM_GPIO_DEFAULT_MODE_EN                   0x80 //[7];
+#define ATOM_GPIO_SETTING_PERMODE_MASK              0x7F //[6:0]
+
+// definitions for ATOM_COMPONENT_VIDEO_INFO.ucLetterBoxMode
+//Line 3 out put 5V.
+#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_A       0x01     //represent gpio 3 state for 16:9
+#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_B       0x02     //represent gpio 4 state for 16:9
+#define ATOM_CV_LINE3_ASPECTRATIO_16_9_GPIO_SHIFT   0x0   
+
+//Line 3 out put 2.2V              
+#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_A 0x04     //represent gpio 3 state for 4:3 Letter box
+#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_B 0x08     //represent gpio 4 state for 4:3 Letter box
+#define ATOM_CV_LINE3_ASPECTRATIO_4_3_LETBOX_GPIO_SHIFT 0x2     
+
+//Line 3 out put 0V
+#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_A        0x10     //represent gpio 3 state for 4:3
+#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_B        0x20     //represent gpio 4 state for 4:3
+#define ATOM_CV_LINE3_ASPECTRATIO_4_3_GPIO_SHIFT    0x4 
+
+#define ATOM_CV_LINE3_ASPECTRATIO_MASK              0x3F     // bit [5:0]
+
+#define ATOM_CV_LINE3_ASPECTRATIO_EXIST             0x80     //bit 7
+
+//GPIO bit index in gpio setting per mode value, also represend the block no. in gpio blocks.
+#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_A   3   //bit 3 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode.
+#define ATOM_GPIO_INDEX_LINE3_ASPECRATIO_GPIO_B   4   //bit 4 in uc480i/uc480p/uc720p/uc1080i, which represend the default gpio bit setting for the mode.
+
+
+typedef struct _ATOM_COMPONENT_VIDEO_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;
+  USHORT             usMask_PinRegisterIndex;
+  USHORT             usEN_PinRegisterIndex;
+  USHORT             usY_PinRegisterIndex;
+  USHORT             usA_PinRegisterIndex;
+  UCHAR              ucBitShift;
+  UCHAR              ucPinActiveState;  //ucPinActiveState: Bit0=1 active high, =0 active low
+  ATOM_DTD_FORMAT    sReserved;         // must be zeroed out
+  UCHAR              ucMiscInfo;
+  UCHAR              uc480i;
+  UCHAR              uc480p;
+  UCHAR              uc720p;
+  UCHAR              uc1080i;
+  UCHAR              ucLetterBoxMode;
+  UCHAR              ucReserved[3];
+  UCHAR              ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector
+  ATOM_GPIO_INFO     aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS];
+  ATOM_DTD_FORMAT    aModeTimings[MAX_SUPPORTED_CV_STANDARDS];
+}ATOM_COMPONENT_VIDEO_INFO;
+
+//ucTableFormatRevision=2
+//ucTableContentRevision=1
+typedef struct _ATOM_COMPONENT_VIDEO_INFO_V21
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;
+  UCHAR              ucMiscInfo;
+  UCHAR              uc480i;
+  UCHAR              uc480p;
+  UCHAR              uc720p;
+  UCHAR              uc1080i;
+  UCHAR              ucReserved;
+  UCHAR              ucLetterBoxMode;
+  UCHAR              ucNumOfWbGpioBlocks; //For Component video D-Connector support. If zere, NTSC type connector
+  ATOM_GPIO_INFO     aWbGpioStateBlock[MAX_SUPPORTED_CV_STANDARDS];
+  ATOM_DTD_FORMAT    aModeTimings[MAX_SUPPORTED_CV_STANDARDS];
+}ATOM_COMPONENT_VIDEO_INFO_V21;
 
 #define ATOM_COMPONENT_VIDEO_INFO_LAST  ATOM_COMPONENT_VIDEO_INFO_V21
 
-/****************************************************************************/
-/*  Structure used in object_InfoTable */
-/****************************************************************************/
-typedef struct _ATOM_OBJECT_HEADER {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usDeviceSupport;
-       USHORT usConnectorObjectTableOffset;
-       USHORT usRouterObjectTableOffset;
-       USHORT usEncoderObjectTableOffset;
-       USHORT usProtectionObjectTableOffset;   /* only available when Protection block is independent. */
-       USHORT usDisplayPathTableOffset;
-} ATOM_OBJECT_HEADER;
-
-typedef struct _ATOM_DISPLAY_OBJECT_PATH {
-       USHORT usDeviceTag;     /* supported device */
-       USHORT usSize;          /* the size of ATOM_DISPLAY_OBJECT_PATH */
-       USHORT usConnObjectId;  /* Connector Object ID */
-       USHORT usGPUObjectId;   /* GPU ID */
-       USHORT usGraphicObjIds[1];      /* 1st Encoder Obj source from GPU to last Graphic Obj destinate to connector. */
-} ATOM_DISPLAY_OBJECT_PATH;
-
-typedef struct _ATOM_DISPLAY_OBJECT_PATH_TABLE {
-       UCHAR ucNumOfDispPath;
-       UCHAR ucVersion;
-       UCHAR ucPadding[2];
-       ATOM_DISPLAY_OBJECT_PATH asDispPath[1];
-} ATOM_DISPLAY_OBJECT_PATH_TABLE;
-
-typedef struct _ATOM_OBJECT    /* each object has this structure */
-{
-       USHORT usObjectID;
-       USHORT usSrcDstTableOffset;
-       USHORT usRecordOffset;  /* this pointing to a bunch of records defined below */
-       USHORT usReserved;
-} ATOM_OBJECT;
-
-typedef struct _ATOM_OBJECT_TABLE      /* Above 4 object table offset pointing to a bunch of objects all have this structure */
-{
-       UCHAR ucNumberOfObjects;
-       UCHAR ucPadding[3];
-       ATOM_OBJECT asObjects[1];
-} ATOM_OBJECT_TABLE;
-
-typedef struct _ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT      /* usSrcDstTableOffset pointing to this structure */
-{
-       UCHAR ucNumberOfSrc;
-       USHORT usSrcObjectID[1];
-       UCHAR ucNumberOfDst;
-       USHORT usDstObjectID[1];
-} ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT;
-
-/* Related definitions, all records are differnt but they have a commond header */
-typedef struct _ATOM_COMMON_RECORD_HEADER {
-       UCHAR ucRecordType;     /* An emun to indicate the record type */
-       UCHAR ucRecordSize;     /* The size of the whole record in byte */
-} ATOM_COMMON_RECORD_HEADER;
-
-#define ATOM_I2C_RECORD_TYPE                           1
+/****************************************************************************/ 
+// Structure used in object_InfoTable
+/****************************************************************************/ 
+typedef struct _ATOM_OBJECT_HEADER
+{ 
+  ATOM_COMMON_TABLE_HEADER     sHeader;
+  USHORT                    usDeviceSupport;
+  USHORT                    usConnectorObjectTableOffset;
+  USHORT                    usRouterObjectTableOffset;
+  USHORT                    usEncoderObjectTableOffset;
+  USHORT                    usProtectionObjectTableOffset; //only available when Protection block is independent.
+  USHORT                    usDisplayPathTableOffset;
+}ATOM_OBJECT_HEADER;
+
+typedef struct _ATOM_OBJECT_HEADER_V3
+{ 
+  ATOM_COMMON_TABLE_HEADER     sHeader;
+  USHORT                    usDeviceSupport;
+  USHORT                    usConnectorObjectTableOffset;
+  USHORT                    usRouterObjectTableOffset;
+  USHORT                    usEncoderObjectTableOffset;
+  USHORT                    usProtectionObjectTableOffset; //only available when Protection block is independent.
+  USHORT                    usDisplayPathTableOffset;
+  USHORT                    usMiscObjectTableOffset;
+}ATOM_OBJECT_HEADER_V3;
+
+typedef struct  _ATOM_DISPLAY_OBJECT_PATH
+{
+  USHORT    usDeviceTag;                                   //supported device 
+  USHORT    usSize;                                        //the size of ATOM_DISPLAY_OBJECT_PATH
+  USHORT    usConnObjectId;                                //Connector Object ID 
+  USHORT    usGPUObjectId;                                 //GPU ID 
+  USHORT    usGraphicObjIds[1];                             //1st Encoder Obj source from GPU to last Graphic Obj destinate to connector.
+}ATOM_DISPLAY_OBJECT_PATH;
+
+typedef struct _ATOM_DISPLAY_OBJECT_PATH_TABLE
+{
+  UCHAR                           ucNumOfDispPath;
+  UCHAR                           ucVersion;
+  UCHAR                           ucPadding[2];
+  ATOM_DISPLAY_OBJECT_PATH        asDispPath[1];
+}ATOM_DISPLAY_OBJECT_PATH_TABLE;
+
+
+typedef struct _ATOM_OBJECT                                //each object has this structure    
+{
+  USHORT              usObjectID;
+  USHORT              usSrcDstTableOffset;
+  USHORT              usRecordOffset;                     //this pointing to a bunch of records defined below
+  USHORT              usReserved;
+}ATOM_OBJECT;
+
+typedef struct _ATOM_OBJECT_TABLE                         //Above 4 object table offset pointing to a bunch of objects all have this structure     
+{
+  UCHAR               ucNumberOfObjects;
+  UCHAR               ucPadding[3];
+  ATOM_OBJECT         asObjects[1];
+}ATOM_OBJECT_TABLE;
+
+typedef struct _ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT         //usSrcDstTableOffset pointing to this structure
+{
+  UCHAR               ucNumberOfSrc;
+  USHORT              usSrcObjectID[1];
+  UCHAR               ucNumberOfDst;
+  USHORT              usDstObjectID[1];
+}ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT;
+
+
+//Two definitions below are for OPM on MXM module designs
+
+#define EXT_HPDPIN_LUTINDEX_0                   0
+#define EXT_HPDPIN_LUTINDEX_1                   1
+#define EXT_HPDPIN_LUTINDEX_2                   2
+#define EXT_HPDPIN_LUTINDEX_3                   3
+#define EXT_HPDPIN_LUTINDEX_4                   4
+#define EXT_HPDPIN_LUTINDEX_5                   5
+#define EXT_HPDPIN_LUTINDEX_6                   6
+#define EXT_HPDPIN_LUTINDEX_7                   7
+#define MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES   (EXT_HPDPIN_LUTINDEX_7+1)
+
+#define EXT_AUXDDC_LUTINDEX_0                   0
+#define EXT_AUXDDC_LUTINDEX_1                   1
+#define EXT_AUXDDC_LUTINDEX_2                   2
+#define EXT_AUXDDC_LUTINDEX_3                   3
+#define EXT_AUXDDC_LUTINDEX_4                   4
+#define EXT_AUXDDC_LUTINDEX_5                   5
+#define EXT_AUXDDC_LUTINDEX_6                   6
+#define EXT_AUXDDC_LUTINDEX_7                   7
+#define MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES   (EXT_AUXDDC_LUTINDEX_7+1)
+
+typedef struct _EXT_DISPLAY_PATH
+{
+  USHORT  usDeviceTag;                    //A bit vector to show what devices are supported 
+  USHORT  usDeviceACPIEnum;               //16bit device ACPI id. 
+  USHORT  usDeviceConnector;              //A physical connector for displays to plug in, using object connector definitions
+  UCHAR   ucExtAUXDDCLutIndex;            //An index into external AUX/DDC channel LUT
+  UCHAR   ucExtHPDPINLutIndex;            //An index into external HPD pin LUT
+  USHORT  usExtEncoderObjId;              //external encoder object id
+  USHORT  usReserved[3]; 
+}EXT_DISPLAY_PATH;
+   
+#define NUMBER_OF_UCHAR_FOR_GUID          16
+#define MAX_NUMBER_OF_EXT_DISPLAY_PATH    7
+
+typedef  struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;
+  UCHAR                    ucGuid [NUMBER_OF_UCHAR_FOR_GUID];     // a GUID is a 16 byte long string
+  EXT_DISPLAY_PATH         sPath[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; // total of fixed 7 entries.
+  UCHAR                    ucChecksum;                            // a  simple Checksum of the sum of whole structure equal to 0x0. 
+  UCHAR                    Reserved [7];                          // for potential expansion
+}ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO;
+
+//Related definitions, all records are differnt but they have a commond header
+typedef struct _ATOM_COMMON_RECORD_HEADER
+{
+  UCHAR               ucRecordType;                      //An emun to indicate the record type
+  UCHAR               ucRecordSize;                      //The size of the whole record in byte
+}ATOM_COMMON_RECORD_HEADER;
+
+
+#define ATOM_I2C_RECORD_TYPE                           1         
 #define ATOM_HPD_INT_RECORD_TYPE                       2
 #define ATOM_OUTPUT_PROTECTION_RECORD_TYPE             3
 #define ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE          4
-#define        ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD_TYPE             5  /* Obsolete, switch to use GPIO_CNTL_RECORD_TYPE */
-#define ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE          6       /* Obsolete, switch to use GPIO_CNTL_RECORD_TYPE */
+#define        ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD_TYPE             5 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE
+#define ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE          6 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE
 #define ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE      7
-#define ATOM_JTAG_RECORD_TYPE                          8       /* Obsolete, switch to use GPIO_CNTL_RECORD_TYPE */
+#define ATOM_JTAG_RECORD_TYPE                          8 //Obsolete, switch to use GPIO_CNTL_RECORD_TYPE
 #define ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE              9
 #define ATOM_ENCODER_DVO_CF_RECORD_TYPE               10
 #define ATOM_CONNECTOR_CF_RECORD_TYPE                 11
 #define        ATOM_CONNECTOR_HARDCODE_DTD_RECORD_TYPE       12
 #define ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE  13
-#define ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE                                14
-#define ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE                                 15
-
-/* Must be updated when new record type is added,equal to that record definition! */
-#define ATOM_MAX_OBJECT_RECORD_NUMBER             ATOM_CONNECTOR_CF_RECORD_TYPE
-
-typedef struct _ATOM_I2C_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       ATOM_I2C_ID_CONFIG sucI2cId;
-       UCHAR ucI2CAddr;        /* The slave address, it's 0 when the record is attached to connector for DDC */
-} ATOM_I2C_RECORD;
-
-typedef struct _ATOM_HPD_INT_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucHPDIntGPIOID;   /* Corresponding block in GPIO_PIN_INFO table gives the pin info */
-       UCHAR ucPlugged_PinState;
-} ATOM_HPD_INT_RECORD;
-
-typedef struct _ATOM_OUTPUT_PROTECTION_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucProtectionFlag;
-       UCHAR ucReserved;
-} ATOM_OUTPUT_PROTECTION_RECORD;
-
-typedef struct _ATOM_CONNECTOR_DEVICE_TAG {
-       ULONG ulACPIDeviceEnum; /* Reserved for now */
-       USHORT usDeviceID;      /* This Id is same as "ATOM_DEVICE_XXX_SUPPORT" */
-       USHORT usPadding;
-} ATOM_CONNECTOR_DEVICE_TAG;
-
-typedef struct _ATOM_CONNECTOR_DEVICE_TAG_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucNumberOfDevice;
-       UCHAR ucReserved;
-       ATOM_CONNECTOR_DEVICE_TAG asDeviceTag[1];       /* This Id is same as "ATOM_DEVICE_XXX_SUPPORT", 1 is only for allocation */
-} ATOM_CONNECTOR_DEVICE_TAG_RECORD;
-
-typedef struct _ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucConfigGPIOID;
-       UCHAR ucConfigGPIOState;        /* Set to 1 when it's active high to enable external flow in */
-       UCHAR ucFlowinGPIPID;
-       UCHAR ucExtInGPIPID;
-} ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD;
-
-typedef struct _ATOM_ENCODER_FPGA_CONTROL_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucCTL1GPIO_ID;
-       UCHAR ucCTL1GPIOState;  /* Set to 1 when it's active high */
-       UCHAR ucCTL2GPIO_ID;
-       UCHAR ucCTL2GPIOState;  /* Set to 1 when it's active high */
-       UCHAR ucCTL3GPIO_ID;
-       UCHAR ucCTL3GPIOState;  /* Set to 1 when it's active high */
-       UCHAR ucCTLFPGA_IN_ID;
-       UCHAR ucPadding[3];
-} ATOM_ENCODER_FPGA_CONTROL_RECORD;
-
-typedef struct _ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucGPIOID;         /* Corresponding block in GPIO_PIN_INFO table gives the pin info */
-       UCHAR ucTVActiveState;  /* Indicating when the pin==0 or 1 when TV is connected */
-} ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD;
-
-typedef struct _ATOM_JTAG_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucTMSGPIO_ID;
-       UCHAR ucTMSGPIOState;   /* Set to 1 when it's active high */
-       UCHAR ucTCKGPIO_ID;
-       UCHAR ucTCKGPIOState;   /* Set to 1 when it's active high */
-       UCHAR ucTDOGPIO_ID;
-       UCHAR ucTDOGPIOState;   /* Set to 1 when it's active high */
-       UCHAR ucTDIGPIO_ID;
-       UCHAR ucTDIGPIOState;   /* Set to 1 when it's active high */
-       UCHAR ucPadding[2];
-} ATOM_JTAG_RECORD;
-
-/* The following generic object gpio pin control record type will replace JTAG_RECORD/FPGA_CONTROL_RECORD/DVI_EXT_INPUT_RECORD above gradually */
-typedef struct _ATOM_GPIO_PIN_CONTROL_PAIR {
-       UCHAR ucGPIOID;         /*  GPIO_ID, find the corresponding ID in GPIO_LUT table */
-       UCHAR ucGPIO_PinState;  /*  Pin state showing how to set-up the pin */
-} ATOM_GPIO_PIN_CONTROL_PAIR;
-
-typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucFlags;          /*  Future expnadibility */
-       UCHAR ucNumberOfPins;   /*  Number of GPIO pins used to control the object */
-       ATOM_GPIO_PIN_CONTROL_PAIR asGpio[1];   /*  the real gpio pin pair determined by number of pins ucNumberOfPins */
-} ATOM_OBJECT_GPIO_CNTL_RECORD;
-
-/* Definitions for GPIO pin state */
+#define ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE              14
+#define ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE 15
+#define ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE          16 //This is for the case when connectors are not known to object table
+#define ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE          17 //This is for the case when connectors are not known to object table
+#define ATOM_OBJECT_LINK_RECORD_TYPE                   18 //Once this record is present under one object, it indicats the oobject is linked to another obj described by the record
+#define ATOM_CONNECTOR_REMOTE_CAP_RECORD_TYPE          19
+
+
+//Must be updated when new record type is added,equal to that record definition!
+#define ATOM_MAX_OBJECT_RECORD_NUMBER             ATOM_CONNECTOR_REMOTE_CAP_RECORD_TYPE
+
+typedef struct  _ATOM_I2C_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  ATOM_I2C_ID_CONFIG          sucI2cId; 
+  UCHAR                       ucI2CAddr;              //The slave address, it's 0 when the record is attached to connector for DDC
+}ATOM_I2C_RECORD;
+
+typedef struct  _ATOM_HPD_INT_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucHPDIntGPIOID;         //Corresponding block in GPIO_PIN_INFO table gives the pin info           
+  UCHAR                       ucPlugged_PinState;
+}ATOM_HPD_INT_RECORD;
+
+
+typedef struct  _ATOM_OUTPUT_PROTECTION_RECORD 
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucProtectionFlag;
+  UCHAR                       ucReserved;
+}ATOM_OUTPUT_PROTECTION_RECORD;
+
+typedef struct  _ATOM_CONNECTOR_DEVICE_TAG
+{
+  ULONG                       ulACPIDeviceEnum;       //Reserved for now
+  USHORT                      usDeviceID;             //This Id is same as "ATOM_DEVICE_XXX_SUPPORT"
+  USHORT                      usPadding;
+}ATOM_CONNECTOR_DEVICE_TAG;
+
+typedef struct  _ATOM_CONNECTOR_DEVICE_TAG_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucNumberOfDevice;
+  UCHAR                       ucReserved;
+  ATOM_CONNECTOR_DEVICE_TAG   asDeviceTag[1];         //This Id is same as "ATOM_DEVICE_XXX_SUPPORT", 1 is only for allocation
+}ATOM_CONNECTOR_DEVICE_TAG_RECORD;
+
+
+typedef struct  _ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                                                            ucConfigGPIOID;
+  UCHAR                                                            ucConfigGPIOState;      //Set to 1 when it's active high to enable external flow in
+  UCHAR                       ucFlowinGPIPID;
+  UCHAR                       ucExtInGPIPID;
+}ATOM_CONNECTOR_DVI_EXT_INPUT_RECORD;
+
+typedef struct  _ATOM_ENCODER_FPGA_CONTROL_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucCTL1GPIO_ID;
+  UCHAR                       ucCTL1GPIOState;        //Set to 1 when it's active high
+  UCHAR                       ucCTL2GPIO_ID;
+  UCHAR                       ucCTL2GPIOState;        //Set to 1 when it's active high
+  UCHAR                       ucCTL3GPIO_ID;
+  UCHAR                       ucCTL3GPIOState;        //Set to 1 when it's active high
+  UCHAR                       ucCTLFPGA_IN_ID;
+  UCHAR                       ucPadding[3];
+}ATOM_ENCODER_FPGA_CONTROL_RECORD;
+
+typedef struct  _ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucGPIOID;               //Corresponding block in GPIO_PIN_INFO table gives the pin info 
+  UCHAR                       ucTVActiveState;        //Indicating when the pin==0 or 1 when TV is connected
+}ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD;
+
+typedef struct  _ATOM_JTAG_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucTMSGPIO_ID;
+  UCHAR                       ucTMSGPIOState;         //Set to 1 when it's active high
+  UCHAR                       ucTCKGPIO_ID;
+  UCHAR                       ucTCKGPIOState;         //Set to 1 when it's active high
+  UCHAR                       ucTDOGPIO_ID;
+  UCHAR                       ucTDOGPIOState;         //Set to 1 when it's active high
+  UCHAR                       ucTDIGPIO_ID;
+  UCHAR                       ucTDIGPIOState;         //Set to 1 when it's active high
+  UCHAR                       ucPadding[2];
+}ATOM_JTAG_RECORD;
+
+
+//The following generic object gpio pin control record type will replace JTAG_RECORD/FPGA_CONTROL_RECORD/DVI_EXT_INPUT_RECORD above gradually
+typedef struct _ATOM_GPIO_PIN_CONTROL_PAIR
+{
+  UCHAR                       ucGPIOID;               // GPIO_ID, find the corresponding ID in GPIO_LUT table
+  UCHAR                       ucGPIO_PinState;        // Pin state showing how to set-up the pin
+}ATOM_GPIO_PIN_CONTROL_PAIR;
+
+typedef struct  _ATOM_OBJECT_GPIO_CNTL_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucFlags;                // Future expnadibility
+  UCHAR                       ucNumberOfPins;         // Number of GPIO pins used to control the object
+  ATOM_GPIO_PIN_CONTROL_PAIR  asGpio[1];              // the real gpio pin pair determined by number of pins ucNumberOfPins
+}ATOM_OBJECT_GPIO_CNTL_RECORD;
+
+//Definitions for GPIO pin state 
 #define GPIO_PIN_TYPE_INPUT             0x00
 #define GPIO_PIN_TYPE_OUTPUT            0x10
 #define GPIO_PIN_TYPE_HW_CONTROL        0x20
 
-/* For GPIO_PIN_TYPE_OUTPUT the following is defined */
+//For GPIO_PIN_TYPE_OUTPUT the following is defined 
 #define GPIO_PIN_OUTPUT_STATE_MASK      0x01
 #define GPIO_PIN_OUTPUT_STATE_SHIFT     0
 #define GPIO_PIN_STATE_ACTIVE_LOW       0x0
 #define GPIO_PIN_STATE_ACTIVE_HIGH      0x1
 
-typedef struct _ATOM_ENCODER_DVO_CF_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       ULONG ulStrengthControl;        /*  DVOA strength control for CF */
-       UCHAR ucPadding[2];
-} ATOM_ENCODER_DVO_CF_RECORD;
+// Indexes to GPIO array in GLSync record 
+#define ATOM_GPIO_INDEX_GLSYNC_REFCLK    0
+#define ATOM_GPIO_INDEX_GLSYNC_HSYNC     1
+#define ATOM_GPIO_INDEX_GLSYNC_VSYNC     2
+#define ATOM_GPIO_INDEX_GLSYNC_SWAP_REQ  3
+#define ATOM_GPIO_INDEX_GLSYNC_SWAP_GNT  4
+#define ATOM_GPIO_INDEX_GLSYNC_INTERRUPT 5
+#define ATOM_GPIO_INDEX_GLSYNC_V_RESET   6
+#define ATOM_GPIO_INDEX_GLSYNC_MAX       7
+
+typedef struct  _ATOM_ENCODER_DVO_CF_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  ULONG                       ulStrengthControl;      // DVOA strength control for CF
+  UCHAR                       ucPadding[2];
+}ATOM_ENCODER_DVO_CF_RECORD;
 
-/*  value for ATOM_CONNECTOR_CF_RECORD.ucConnectedDvoBundle */
+// value for ATOM_CONNECTOR_CF_RECORD.ucConnectedDvoBundle
 #define ATOM_CONNECTOR_CF_RECORD_CONNECTED_UPPER12BITBUNDLEA   1
 #define ATOM_CONNECTOR_CF_RECORD_CONNECTED_LOWER12BITBUNDLEB   2
 
-typedef struct _ATOM_CONNECTOR_CF_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       USHORT usMaxPixClk;
-       UCHAR ucFlowCntlGpioId;
-       UCHAR ucSwapCntlGpioId;
-       UCHAR ucConnectedDvoBundle;
-       UCHAR ucPadding;
-} ATOM_CONNECTOR_CF_RECORD;
-
-typedef struct _ATOM_CONNECTOR_HARDCODE_DTD_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       ATOM_DTD_FORMAT asTiming;
-} ATOM_CONNECTOR_HARDCODE_DTD_RECORD;
-
-typedef struct _ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;      /* ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE */
-       UCHAR ucSubConnectorType;       /* CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D|X_ID_DUAL_LINK_DVI_D|HDMI_TYPE_A */
-       UCHAR ucReserved;
-} ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD;
-
-typedef struct _ATOM_ROUTER_DDC_PATH_SELECT_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucMuxType;        /* decide the number of ucMuxState, =0, no pin state, =1: single state with complement, >1: multiple state */
-       UCHAR ucMuxControlPin;
-       UCHAR ucMuxState[2];    /* for alligment purpose */
-} ATOM_ROUTER_DDC_PATH_SELECT_RECORD;
-
-typedef struct _ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD {
-       ATOM_COMMON_RECORD_HEADER sheader;
-       UCHAR ucMuxType;
-       UCHAR ucMuxControlPin;
-       UCHAR ucMuxState[2];    /* for alligment purpose */
-} ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD;
-
-/*  define ucMuxType */
+typedef struct  _ATOM_CONNECTOR_CF_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  USHORT                      usMaxPixClk;
+  UCHAR                       ucFlowCntlGpioId;
+  UCHAR                       ucSwapCntlGpioId;
+  UCHAR                       ucConnectedDvoBundle;
+  UCHAR                       ucPadding;
+}ATOM_CONNECTOR_CF_RECORD;
+
+typedef struct  _ATOM_CONNECTOR_HARDCODE_DTD_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+       ATOM_DTD_FORMAT                                                 asTiming;
+}ATOM_CONNECTOR_HARDCODE_DTD_RECORD;
+
+typedef struct _ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;                //ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE
+  UCHAR                       ucSubConnectorType;     //CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D|X_ID_DUAL_LINK_DVI_D|HDMI_TYPE_A
+  UCHAR                       ucReserved;
+}ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD;
+
+
+typedef struct _ATOM_ROUTER_DDC_PATH_SELECT_RECORD
+{
+       ATOM_COMMON_RECORD_HEADER   sheader;                
+       UCHAR                                                                                           ucMuxType;                                                      //decide the number of ucMuxState, =0, no pin state, =1: single state with complement, >1: multiple state
+       UCHAR                                                                                           ucMuxControlPin;
+       UCHAR                                                                                           ucMuxState[2];                                  //for alligment purpose
+}ATOM_ROUTER_DDC_PATH_SELECT_RECORD;
+
+typedef struct _ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD
+{
+       ATOM_COMMON_RECORD_HEADER   sheader;                
+       UCHAR                                                                                           ucMuxType;
+       UCHAR                                                                                           ucMuxControlPin;
+       UCHAR                                                                                           ucMuxState[2];                                  //for alligment purpose
+}ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD;
+
+// define ucMuxType
 #define ATOM_ROUTER_MUX_PIN_STATE_MASK                                                         0x0f
 #define ATOM_ROUTER_MUX_PIN_SINGLE_STATE_COMPLEMENT            0x01
 
-/****************************************************************************/
-/*  ASIC voltage data table */
-/****************************************************************************/
-typedef struct _ATOM_VOLTAGE_INFO_HEADER {
-       USHORT usVDDCBaseLevel; /* In number of 50mv unit */
-       USHORT usReserved;      /* For possible extension table offset */
-       UCHAR ucNumOfVoltageEntries;
-       UCHAR ucBytesPerVoltageEntry;
-       UCHAR ucVoltageStep;    /* Indicating in how many mv increament is one step, 0.5mv unit */
-       UCHAR ucDefaultVoltageEntry;
-       UCHAR ucVoltageControlI2cLine;
-       UCHAR ucVoltageControlAddress;
-       UCHAR ucVoltageControlOffset;
-} ATOM_VOLTAGE_INFO_HEADER;
-
-typedef struct _ATOM_VOLTAGE_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_VOLTAGE_INFO_HEADER viHeader;
-       UCHAR ucVoltageEntries[64];     /* 64 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries*ucBytesPerVoltageEntry */
-} ATOM_VOLTAGE_INFO;
-
-typedef struct _ATOM_VOLTAGE_FORMULA {
-       USHORT usVoltageBaseLevel;      /*  In number of 1mv unit */
-       USHORT usVoltageStep;   /*  Indicating in how many mv increament is one step, 1mv unit */
-       UCHAR ucNumOfVoltageEntries;    /*  Number of Voltage Entry, which indicate max Voltage */
-       UCHAR ucFlag;           /*  bit0=0 :step is 1mv =1 0.5mv */
-       UCHAR ucBaseVID;        /*  if there is no lookup table, VID= BaseVID + ( Vol - BaseLevle ) /VoltageStep */
-       UCHAR ucReserved;
-       UCHAR ucVIDAdjustEntries[32];   /*  32 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries */
-} ATOM_VOLTAGE_FORMULA;
-
-typedef struct _ATOM_VOLTAGE_CONTROL {
-       UCHAR ucVoltageControlId;       /* Indicate it is controlled by I2C or GPIO or HW state machine */
-       UCHAR ucVoltageControlI2cLine;
-       UCHAR ucVoltageControlAddress;
-       UCHAR ucVoltageControlOffset;
-       USHORT usGpioPin_AIndex;        /* GPIO_PAD register index */
-       UCHAR ucGpioPinBitShift[9];     /* at most 8 pin support 255 VIDs, termintate with 0xff */
-       UCHAR ucReserved;
-} ATOM_VOLTAGE_CONTROL;
-
-/*  Define ucVoltageControlId */
+typedef struct _ATOM_CONNECTOR_HPDPIN_LUT_RECORD     //record for ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  UCHAR                       ucHPDPINMap[MAX_NUMBER_OF_EXT_HPDPIN_LUT_ENTRIES];  //An fixed size array which maps external pins to internal GPIO_PIN_INFO table 
+}ATOM_CONNECTOR_HPDPIN_LUT_RECORD;
+
+typedef struct _ATOM_CONNECTOR_AUXDDC_LUT_RECORD  //record for ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  ATOM_I2C_ID_CONFIG          ucAUXDDCMap[MAX_NUMBER_OF_EXT_AUXDDC_LUT_ENTRIES];  //An fixed size array which maps external pins to internal DDC ID
+}ATOM_CONNECTOR_AUXDDC_LUT_RECORD;
+
+typedef struct _ATOM_OBJECT_LINK_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  USHORT                      usObjectID;         //could be connector, encorder or other object in object.h
+}ATOM_OBJECT_LINK_RECORD;
+
+typedef struct _ATOM_CONNECTOR_REMOTE_CAP_RECORD
+{
+  ATOM_COMMON_RECORD_HEADER   sheader;
+  USHORT                      usReserved;
+}ATOM_CONNECTOR_REMOTE_CAP_RECORD;
+
+/****************************************************************************/ 
+// ASIC voltage data table
+/****************************************************************************/ 
+typedef struct  _ATOM_VOLTAGE_INFO_HEADER
+{
+   USHORT   usVDDCBaseLevel;                //In number of 50mv unit
+   USHORT   usReserved;                     //For possible extension table offset
+   UCHAR    ucNumOfVoltageEntries;
+   UCHAR    ucBytesPerVoltageEntry;
+   UCHAR    ucVoltageStep;                  //Indicating in how many mv increament is one step, 0.5mv unit
+   UCHAR    ucDefaultVoltageEntry;
+   UCHAR    ucVoltageControlI2cLine;
+   UCHAR    ucVoltageControlAddress;
+   UCHAR    ucVoltageControlOffset;
+}ATOM_VOLTAGE_INFO_HEADER;
+
+typedef struct  _ATOM_VOLTAGE_INFO
+{
+   ATOM_COMMON_TABLE_HEADER    sHeader; 
+   ATOM_VOLTAGE_INFO_HEADER viHeader;
+   UCHAR    ucVoltageEntries[64];            //64 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries*ucBytesPerVoltageEntry
+}ATOM_VOLTAGE_INFO;
+
+
+typedef struct  _ATOM_VOLTAGE_FORMULA
+{
+   USHORT   usVoltageBaseLevel;             // In number of 1mv unit
+   USHORT   usVoltageStep;                  // Indicating in how many mv increament is one step, 1mv unit
+        UCHAR          ucNumOfVoltageEntries;                                  // Number of Voltage Entry, which indicate max Voltage
+        UCHAR          ucFlag;                                                                                                 // bit0=0 :step is 1mv =1 0.5mv
+        UCHAR          ucBaseVID;                                                                                      // if there is no lookup table, VID= BaseVID + ( Vol - BaseLevle ) /VoltageStep
+        UCHAR          ucReserved;
+        UCHAR          ucVIDAdjustEntries[32];                                 // 32 is for allocation, the actual number of entry is present at ucNumOfVoltageEntries
+}ATOM_VOLTAGE_FORMULA;
+
+typedef struct  _VOLTAGE_LUT_ENTRY
+{
+        USHORT         usVoltageCode;                                                                  // The Voltage ID, either GPIO or I2C code
+        USHORT         usVoltageValue;                                                                 // The corresponding Voltage Value, in mV
+}VOLTAGE_LUT_ENTRY;
+
+typedef struct  _ATOM_VOLTAGE_FORMULA_V2
+{
+        UCHAR          ucNumOfVoltageEntries;                                  // Number of Voltage Entry, which indicate max Voltage
+        UCHAR          ucReserved[3];
+        VOLTAGE_LUT_ENTRY asVIDAdjustEntries[32];// 32 is for allocation, the actual number of entries is in ucNumOfVoltageEntries
+}ATOM_VOLTAGE_FORMULA_V2;
+
+typedef struct _ATOM_VOLTAGE_CONTROL
+{
+       UCHAR            ucVoltageControlId;                                                    //Indicate it is controlled by I2C or GPIO or HW state machine          
+  UCHAR    ucVoltageControlI2cLine;
+  UCHAR    ucVoltageControlAddress;
+  UCHAR    ucVoltageControlOffset;             
+  USHORT   usGpioPin_AIndex;                                                           //GPIO_PAD register index
+  UCHAR    ucGpioPinBitShift[9];                                               //at most 8 pin support 255 VIDs, termintate with 0xff
+       UCHAR            ucReserved;
+}ATOM_VOLTAGE_CONTROL;
+
+// Define ucVoltageControlId
 #define        VOLTAGE_CONTROLLED_BY_HW                                                        0x00
 #define        VOLTAGE_CONTROLLED_BY_I2C_MASK                          0x7F
 #define        VOLTAGE_CONTROLLED_BY_GPIO                                              0x80
-#define        VOLTAGE_CONTROL_ID_LM64                                                         0x01    /* I2C control, used for R5xx Core Voltage */
-#define        VOLTAGE_CONTROL_ID_DAC                                                          0x02    /* I2C control, used for R5xx/R6xx MVDDC,MVDDQ or VDDCI */
-#define        VOLTAGE_CONTROL_ID_VT116xM                                              0x03    /* I2C control, used for R6xx Core Voltage */
-#define VOLTAGE_CONTROL_ID_DS4402                                                      0x04
-
-typedef struct _ATOM_VOLTAGE_OBJECT {
-       UCHAR ucVoltageType;    /* Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI */
-       UCHAR ucSize;           /* Size of Object */
-       ATOM_VOLTAGE_CONTROL asControl; /* describ how to control */
-       ATOM_VOLTAGE_FORMULA asFormula; /* Indicate How to convert real Voltage to VID */
-} ATOM_VOLTAGE_OBJECT;
-
-typedef struct _ATOM_VOLTAGE_OBJECT_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_VOLTAGE_OBJECT asVoltageObj[3];    /* Info for Voltage control */
-} ATOM_VOLTAGE_OBJECT_INFO;
-
-typedef struct _ATOM_LEAKID_VOLTAGE {
-       UCHAR ucLeakageId;
-       UCHAR ucReserved;
-       USHORT usVoltage;
-} ATOM_LEAKID_VOLTAGE;
-
-typedef struct _ATOM_ASIC_PROFILE_VOLTAGE {
-       UCHAR ucProfileId;
-       UCHAR ucReserved;
-       USHORT usSize;
-       USHORT usEfuseSpareStartAddr;
-       USHORT usFuseIndex[8];  /* from LSB to MSB, Max 8bit,end of 0xffff if less than 8 efuse id, */
-       ATOM_LEAKID_VOLTAGE asLeakVol[2];       /* Leakid and relatd voltage */
-} ATOM_ASIC_PROFILE_VOLTAGE;
-
-/* ucProfileId */
-#define        ATOM_ASIC_PROFILE_ID_EFUSE_VOLTAGE                      1
+#define        VOLTAGE_CONTROL_ID_LM64                                                         0x01                                                                    //I2C control, used for R5xx Core Voltage
+#define        VOLTAGE_CONTROL_ID_DAC                                                          0x02                                                                    //I2C control, used for R5xx/R6xx MVDDC,MVDDQ or VDDCI
+#define        VOLTAGE_CONTROL_ID_VT116xM                                              0x03                                                                    //I2C control, used for R6xx Core Voltage
+#define VOLTAGE_CONTROL_ID_DS4402                                                      0x04                                                                    
+
+typedef struct  _ATOM_VOLTAGE_OBJECT
+{
+        UCHAR          ucVoltageType;                                                                  //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI  
+        UCHAR          ucSize;                                                                                                 //Size of Object        
+        ATOM_VOLTAGE_CONTROL                   asControl;                      //describ how to control         
+        ATOM_VOLTAGE_FORMULA                   asFormula;                      //Indicate How to convert real Voltage to VID 
+}ATOM_VOLTAGE_OBJECT;
+
+typedef struct  _ATOM_VOLTAGE_OBJECT_V2
+{
+        UCHAR          ucVoltageType;                                                                  //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI  
+        UCHAR          ucSize;                                                                                                 //Size of Object        
+        ATOM_VOLTAGE_CONTROL                   asControl;                      //describ how to control         
+        ATOM_VOLTAGE_FORMULA_V2        asFormula;                      //Indicate How to convert real Voltage to VID 
+}ATOM_VOLTAGE_OBJECT_V2;
+
+typedef struct  _ATOM_VOLTAGE_OBJECT_INFO
+{
+   ATOM_COMMON_TABLE_HEADER    sHeader; 
+        ATOM_VOLTAGE_OBJECT                    asVoltageObj[3];        //Info for Voltage control               
+}ATOM_VOLTAGE_OBJECT_INFO;
+
+typedef struct  _ATOM_VOLTAGE_OBJECT_INFO_V2
+{
+   ATOM_COMMON_TABLE_HEADER    sHeader; 
+        ATOM_VOLTAGE_OBJECT_V2                 asVoltageObj[3];        //Info for Voltage control               
+}ATOM_VOLTAGE_OBJECT_INFO_V2;
+
+typedef struct  _ATOM_LEAKID_VOLTAGE
+{
+       UCHAR           ucLeakageId;
+       UCHAR           ucReserved;
+       USHORT  usVoltage;
+}ATOM_LEAKID_VOLTAGE;
+
+typedef struct  _ATOM_ASIC_PROFILE_VOLTAGE
+{
+       UCHAR           ucProfileId;
+       UCHAR           ucReserved;
+       USHORT  usSize;
+       USHORT  usEfuseSpareStartAddr;
+       USHORT  usFuseIndex[8];                                                                                         //from LSB to MSB, Max 8bit,end of 0xffff if less than 8 efuse id, 
+       ATOM_LEAKID_VOLTAGE                                     asLeakVol[2];                   //Leakid and relatd voltage
+}ATOM_ASIC_PROFILE_VOLTAGE;
+
+//ucProfileId
+#define        ATOM_ASIC_PROFILE_ID_EFUSE_VOLTAGE                      1               
 #define        ATOM_ASIC_PROFILE_ID_EFUSE_PERFORMANCE_VOLTAGE                  1
 #define        ATOM_ASIC_PROFILE_ID_EFUSE_THERMAL_VOLTAGE                                      2
 
-typedef struct _ATOM_ASIC_PROFILING_INFO {
-       ATOM_COMMON_TABLE_HEADER asHeader;
-       ATOM_ASIC_PROFILE_VOLTAGE asVoltage;
-} ATOM_ASIC_PROFILING_INFO;
-
-typedef struct _ATOM_POWER_SOURCE_OBJECT {
-       UCHAR ucPwrSrcId;       /*  Power source */
-       UCHAR ucPwrSensorType;  /*  GPIO, I2C or none */
-       UCHAR ucPwrSensId;      /*  if GPIO detect, it is GPIO id,  if I2C detect, it is I2C id */
-       UCHAR ucPwrSensSlaveAddr;       /*  Slave address if I2C detect */
-       UCHAR ucPwrSensRegIndex;        /*  I2C register Index if I2C detect */
-       UCHAR ucPwrSensRegBitMask;      /*  detect which bit is used if I2C detect */
-       UCHAR ucPwrSensActiveState;     /*  high active or low active */
-       UCHAR ucReserve[3];     /*  reserve */
-       USHORT usSensPwr;       /*  in unit of watt */
-} ATOM_POWER_SOURCE_OBJECT;
-
-typedef struct _ATOM_POWER_SOURCE_INFO {
-       ATOM_COMMON_TABLE_HEADER asHeader;
-       UCHAR asPwrbehave[16];
-       ATOM_POWER_SOURCE_OBJECT asPwrObj[1];
-} ATOM_POWER_SOURCE_INFO;
-
-/* Define ucPwrSrcId */
+typedef struct  _ATOM_ASIC_PROFILING_INFO
+{
+  ATOM_COMMON_TABLE_HEADER                     asHeader; 
+       ATOM_ASIC_PROFILE_VOLTAGE                       asVoltage;
+}ATOM_ASIC_PROFILING_INFO;
+
+typedef struct _ATOM_POWER_SOURCE_OBJECT
+{
+       UCHAR   ucPwrSrcId;                                                                                                     // Power source
+       UCHAR   ucPwrSensorType;                                                                                // GPIO, I2C or none
+       UCHAR   ucPwrSensId;                                                                                      // if GPIO detect, it is GPIO id,  if I2C detect, it is I2C id
+       UCHAR   ucPwrSensSlaveAddr;                                                                     // Slave address if I2C detect
+       UCHAR ucPwrSensRegIndex;                                                                        // I2C register Index if I2C detect
+       UCHAR ucPwrSensRegBitMask;                                                              // detect which bit is used if I2C detect
+       UCHAR   ucPwrSensActiveState;                                                           // high active or low active
+       UCHAR   ucReserve[3];                                                                                           // reserve              
+       USHORT usSensPwr;                                                                                                       // in unit of watt
+}ATOM_POWER_SOURCE_OBJECT;
+
+typedef struct _ATOM_POWER_SOURCE_INFO
+{
+               ATOM_COMMON_TABLE_HEADER                asHeader;
+               UCHAR                                                                                           asPwrbehave[16];
+               ATOM_POWER_SOURCE_OBJECT                asPwrObj[1];
+}ATOM_POWER_SOURCE_INFO;
+
+
+//Define ucPwrSrcId
 #define POWERSOURCE_PCIE_ID1                                           0x00
 #define POWERSOURCE_6PIN_CONNECTOR_ID1 0x01
 #define POWERSOURCE_8PIN_CONNECTOR_ID1 0x02
 #define POWERSOURCE_6PIN_CONNECTOR_ID2 0x04
 #define POWERSOURCE_8PIN_CONNECTOR_ID2 0x08
 
-/* define ucPwrSensorId */
+//define ucPwrSensorId
 #define POWER_SENSOR_ALWAYS                                                    0x00
 #define POWER_SENSOR_GPIO                                                              0x01
 #define POWER_SENSOR_I2C                                                               0x02
 
+typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+  ULONG  ulBootUpEngineClock;
+  ULONG  ulDentistVCOFreq;          
+  ULONG  ulBootUpUMAClock;          
+  ULONG  ulReserved1[8];            
+  ULONG  ulBootUpReqDisplayVector;
+  ULONG  ulOtherDisplayMisc;
+  ULONG  ulGPUCapInfo;
+  ULONG  ulReserved2[3];            
+  ULONG  ulSystemConfig;            
+  ULONG  ulCPUCapInfo;              
+  USHORT usMaxNBVoltage;  
+  USHORT usMinNBVoltage;  
+  USHORT usBootUpNBVoltage;         
+  USHORT usExtDispConnInfoOffset;  
+  UCHAR  ucHtcTmpLmt;   
+  UCHAR  ucTjOffset;    
+  UCHAR  ucMemoryType;  
+  UCHAR  ucUMAChannelNumber;
+  ULONG  ulCSR_M3_ARB_CNTL_DEFAULT[10];  
+  ULONG  ulCSR_M3_ARB_CNTL_UVD[10]; 
+  ULONG  ulCSR_M3_ARB_CNTL_FS3D[10];
+  ULONG  ulReserved3[42]; 
+  ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo;   
+}ATOM_INTEGRATED_SYSTEM_INFO_V6;   
+
+/**********************************************************************************************************************
+// ATOM_INTEGRATED_SYSTEM_INFO_V6 Description
+//ulBootUpEngineClock:              VBIOS bootup Engine clock frequency, in 10kHz unit. 
+//ulDentistVCOFreq:                 Dentist VCO clock in 10kHz unit. 
+//ulBootUpUMAClock:                 System memory boot up clock frequency in 10Khz unit. 
+//ulReserved1[8]                    Reserved by now, must be 0x0. 
+//ulBootUpReqDisplayVector             VBIOS boot up display IDs
+//                                  ATOM_DEVICE_CRT1_SUPPORT                  0x0001
+//                                  ATOM_DEVICE_CRT2_SUPPORT                  0x0010
+//                                  ATOM_DEVICE_DFP1_SUPPORT                  0x0008 
+//                                  ATOM_DEVICE_DFP6_SUPPORT                  0x0040 
+//                                  ATOM_DEVICE_DFP2_SUPPORT                  0x0080       
+//                                  ATOM_DEVICE_DFP3_SUPPORT                  0x0200       
+//                                  ATOM_DEVICE_DFP4_SUPPORT                  0x0400        
+//                                  ATOM_DEVICE_DFP5_SUPPORT                  0x0800
+//                                  ATOM_DEVICE_LCD1_SUPPORT                  0x0002
+//ulOtherDisplayMisc                   Other display related flags, not defined yet. 
+//ulGPUCapInfo                      TBD
+//ulReserved2[3]                    must be 0x0 for the reserved.
+//ulSystemConfig                    TBD
+//ulCPUCapInfo                      TBD
+//usMaxNBVoltage                    High NB voltage in unit of mv, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse. 
+//usMinNBVoltage                    Low NB voltage in unit of mv, calculated using current VDDNB (D24F2xDC) and VDDNB offset fuse.
+//usBootUpNBVoltage                 Boot up NB voltage in unit of mv.
+//ucHtcTmpLmt                       Bit [22:16] of D24F3x64 Thermal Control (HTC) Register.
+//ucTjOffset                        Bit [28:22] of D24F3xE4 Thermtrip Status Register,may not be needed.
+//ucMemoryType                      [3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved.
+//ucUMAChannelNumber                   System memory channel numbers. 
+//usExtDispConnectionInfoOffset     ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO offset relative to beginning of this table. 
+//ulCSR_M3_ARB_CNTL_DEFAULT[10]     Arrays with values for CSR M3 arbiter for default
+//ulCSR_M3_ARB_CNTL_UVD[10]         Arrays with values for CSR M3 arbiter for UVD playback.
+//ulCSR_M3_ARB_CNTL_FS3D[10]        Arrays with values for CSR M3 arbiter for Full Screen 3D applications.
+**********************************************************************************************************************/
+
 /**************************************************************************/
-/*  This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design */
-/* Memory SS Info Table */
-/* Define Memory Clock SS chip ID */
+// This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design
+//Memory SS Info Table
+//Define Memory Clock SS chip ID
 #define ICS91719  1
 #define ICS91720  2
 
-/* Define one structure to inform SW a "block of data" writing to external SS chip via I2C protocol */
-typedef struct _ATOM_I2C_DATA_RECORD {
-       UCHAR ucNunberOfBytes;  /* Indicates how many bytes SW needs to write to the external ASIC for one block, besides to "Start" and "Stop" */
-       UCHAR ucI2CData[1];     /* I2C data in bytes, should be less than 16 bytes usually */
-} ATOM_I2C_DATA_RECORD;
-
-/* Define one structure to inform SW how many blocks of data writing to external SS chip via I2C protocol, in addition to other information */
-typedef struct _ATOM_I2C_DEVICE_SETUP_INFO {
-       ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;     /* I2C line and HW/SW assisted cap. */
-       UCHAR ucSSChipID;       /* SS chip being used */
-       UCHAR ucSSChipSlaveAddr;        /* Slave Address to set up this SS chip */
-       UCHAR ucNumOfI2CDataRecords;    /* number of data block */
-       ATOM_I2C_DATA_RECORD asI2CData[1];
-} ATOM_I2C_DEVICE_SETUP_INFO;
-
-/* ========================================================================================== */
-typedef struct _ATOM_ASIC_MVDD_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_I2C_DEVICE_SETUP_INFO asI2CSetup[1];
-} ATOM_ASIC_MVDD_INFO;
-
-/* ========================================================================================== */
+//Define one structure to inform SW a "block of data" writing to external SS chip via I2C protocol
+typedef struct _ATOM_I2C_DATA_RECORD
+{
+  UCHAR         ucNunberOfBytes;                                              //Indicates how many bytes SW needs to write to the external ASIC for one block, besides to "Start" and "Stop"
+  UCHAR         ucI2CData[1];                                                 //I2C data in bytes, should be less than 16 bytes usually
+}ATOM_I2C_DATA_RECORD;
+
+
+//Define one structure to inform SW how many blocks of data writing to external SS chip via I2C protocol, in addition to other information
+typedef struct _ATOM_I2C_DEVICE_SETUP_INFO
+{
+  ATOM_I2C_ID_CONFIG_ACCESS       sucI2cId;               //I2C line and HW/SW assisted cap.
+  UCHAR                                        ucSSChipID;             //SS chip being used
+  UCHAR                                        ucSSChipSlaveAddr;      //Slave Address to set up this SS chip
+  UCHAR                           ucNumOfI2CDataRecords;  //number of data block
+  ATOM_I2C_DATA_RECORD            asI2CData[1];  
+}ATOM_I2C_DEVICE_SETUP_INFO;
+
+//==========================================================================================
+typedef struct  _ATOM_ASIC_MVDD_INFO
+{
+  ATOM_COMMON_TABLE_HEADER           sHeader; 
+  ATOM_I2C_DEVICE_SETUP_INFO      asI2CSetup[1];
+}ATOM_ASIC_MVDD_INFO;
+
+//==========================================================================================
 #define ATOM_MCLK_SS_INFO         ATOM_ASIC_MVDD_INFO
 
-/* ========================================================================================== */
+//==========================================================================================
 /**************************************************************************/
 
-typedef struct _ATOM_ASIC_SS_ASSIGNMENT {
-       ULONG ulTargetClockRange;       /* Clock Out frequence (VCO ), in unit of 10Khz */
-       USHORT usSpreadSpectrumPercentage;      /* in unit of 0.01% */
-       USHORT usSpreadRateInKhz;       /* in unit of kHz, modulation freq */
-       UCHAR ucClockIndication;        /* Indicate which clock source needs SS */
-       UCHAR ucSpreadSpectrumMode;     /* Bit1=0 Down Spread,=1 Center Spread. */
-       UCHAR ucReserved[2];
-} ATOM_ASIC_SS_ASSIGNMENT;
-
-/* Define ucSpreadSpectrumType */
+typedef struct _ATOM_ASIC_SS_ASSIGNMENT
+{
+       ULONG                                                           ulTargetClockRange;                                             //Clock Out frequence (VCO ), in unit of 10Khz
+  USHORT              usSpreadSpectrumPercentage;              //in unit of 0.01%
+       USHORT                                                  usSpreadRateInKhz;                                              //in unit of kHz, modulation freq
+  UCHAR               ucClockIndication;                                         //Indicate which clock source needs SS
+       UCHAR                                                           ucSpreadSpectrumMode;                                   //Bit1=0 Down Spread,=1 Center Spread.
+       UCHAR                                                           ucReserved[2];
+}ATOM_ASIC_SS_ASSIGNMENT;
+
+//Define ucClockIndication, SW uses the IDs below to search if the SS is requried/enabled on a clock branch/signal type.
+//SS is not required or enabled if a match is not found.
 #define ASIC_INTERNAL_MEMORY_SS                        1
 #define ASIC_INTERNAL_ENGINE_SS                        2
-#define ASIC_INTERNAL_UVD_SS                           3
+#define ASIC_INTERNAL_UVD_SS        3
+#define ASIC_INTERNAL_SS_ON_TMDS    4
+#define ASIC_INTERNAL_SS_ON_HDMI    5
+#define ASIC_INTERNAL_SS_ON_LVDS    6
+#define ASIC_INTERNAL_SS_ON_DP      7
+#define ASIC_INTERNAL_SS_ON_DCPLL   8
+
+typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V2
+{
+       ULONG                                                           ulTargetClockRange;                                             //For mem/engine/uvd, Clock Out frequence (VCO ), in unit of 10Khz
+                                                    //For TMDS/HDMI/LVDS, it is pixel clock , for DP, it is link clock ( 27000 or 16200 )
+  USHORT              usSpreadSpectrumPercentage;              //in unit of 0.01%
+       USHORT                                                  usSpreadRateIn10Hz;                                             //in unit of 10Hz, modulation freq
+  UCHAR               ucClockIndication;                                         //Indicate which clock source needs SS
+       UCHAR                                                           ucSpreadSpectrumMode;                                   //Bit0=0 Down Spread,=1 Center Spread, bit1=0: internal SS bit1=1: external SS
+       UCHAR                                                           ucReserved[2];
+}ATOM_ASIC_SS_ASSIGNMENT_V2;
+
+//ucSpreadSpectrumMode
+//#define ATOM_SS_DOWN_SPREAD_MODE_MASK          0x00000000
+//#define ATOM_SS_DOWN_SPREAD_MODE               0x00000000
+//#define ATOM_SS_CENTRE_SPREAD_MODE_MASK        0x00000001
+//#define ATOM_SS_CENTRE_SPREAD_MODE             0x00000001
+//#define ATOM_INTERNAL_SS_MASK                  0x00000000
+//#define ATOM_EXTERNAL_SS_MASK                  0x00000002
+
+typedef struct _ATOM_ASIC_INTERNAL_SS_INFO
+{
+  ATOM_COMMON_TABLE_HEADER           sHeader; 
+  ATOM_ASIC_SS_ASSIGNMENT                    asSpreadSpectrum[4];
+}ATOM_ASIC_INTERNAL_SS_INFO;
+
+typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V2
+{
+  ATOM_COMMON_TABLE_HEADER           sHeader; 
+  ATOM_ASIC_SS_ASSIGNMENT_V2             asSpreadSpectrum[1];      //this is point only. 
+}ATOM_ASIC_INTERNAL_SS_INFO_V2;
+
+typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V3
+{
+       ULONG                                                           ulTargetClockRange;                                             //For mem/engine/uvd, Clock Out frequence (VCO ), in unit of 10Khz
+                                                    //For TMDS/HDMI/LVDS, it is pixel clock , for DP, it is link clock ( 27000 or 16200 )
+  USHORT              usSpreadSpectrumPercentage;              //in unit of 0.01%
+       USHORT                                                  usSpreadRateIn10Hz;                                             //in unit of 10Hz, modulation freq
+  UCHAR               ucClockIndication;                                         //Indicate which clock source needs SS
+       UCHAR                                                           ucSpreadSpectrumMode;                                   //Bit0=0 Down Spread,=1 Center Spread, bit1=0: internal SS bit1=1: external SS
+       UCHAR                                                           ucReserved[2];
+}ATOM_ASIC_SS_ASSIGNMENT_V3;
+
+typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3
+{
+  ATOM_COMMON_TABLE_HEADER           sHeader; 
+  ATOM_ASIC_SS_ASSIGNMENT_V3             asSpreadSpectrum[1];      //this is pointer only. 
+}ATOM_ASIC_INTERNAL_SS_INFO_V3;
 
-typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_ASIC_SS_ASSIGNMENT asSpreadSpectrum[4];
-} ATOM_ASIC_INTERNAL_SS_INFO;
 
-/* ==============================Scratch Pad Definition Portion=============================== */
+//==============================Scratch Pad Definition Portion===============================
 #define ATOM_DEVICE_CONNECT_INFO_DEF  0
 #define ATOM_ROM_LOCATION_DEF         1
 #define ATOM_TV_STANDARD_DEF          2
@@ -2995,7 +3852,8 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_I2C_CHANNEL_STATUS_DEF   8
 #define ATOM_I2C_CHANNEL_STATUS1_DEF  9
 
-/*  BIOS_0_SCRATCH Definition */
+
+// BIOS_0_SCRATCH Definition 
 #define ATOM_S0_CRT1_MONO               0x00000001L
 #define ATOM_S0_CRT1_COLOR              0x00000002L
 #define ATOM_S0_CRT1_MASK               (ATOM_S0_CRT1_MONO+ATOM_S0_CRT1_COLOR)
@@ -3008,6 +3866,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S0_CV_DIN_A                0x00000020L
 #define ATOM_S0_CV_MASK_A               (ATOM_S0_CV_A+ATOM_S0_CV_DIN_A)
 
+
 #define ATOM_S0_CRT2_MONO               0x00000100L
 #define ATOM_S0_CRT2_COLOR              0x00000200L
 #define ATOM_S0_CRT2_MASK               (ATOM_S0_CRT2_MONO+ATOM_S0_CRT2_COLOR)
@@ -3025,28 +3884,27 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S0_DFP2                    0x00020000L
 #define ATOM_S0_LCD1                    0x00040000L
 #define ATOM_S0_LCD2                    0x00080000L
-#define ATOM_S0_TV2                     0x00100000L
-#define ATOM_S0_DFP3                   0x00200000L
-#define ATOM_S0_DFP4                   0x00400000L
-#define ATOM_S0_DFP5                   0x00800000L
+#define ATOM_S0_DFP6                    0x00100000L
+#define ATOM_S0_DFP3                    0x00200000L
+#define ATOM_S0_DFP4                    0x00400000L
+#define ATOM_S0_DFP5                    0x00800000L
 
-#define ATOM_S0_DFP_MASK \
-       (ATOM_S0_DFP1 | ATOM_S0_DFP2 | ATOM_S0_DFP3 | ATOM_S0_DFP4 | ATOM_S0_DFP5)
+#define ATOM_S0_DFP_MASK                ATOM_S0_DFP1 | ATOM_S0_DFP2 | ATOM_S0_DFP3 | ATOM_S0_DFP4 | ATOM_S0_DFP5 | ATOM_S0_DFP6
 
-#define ATOM_S0_FAD_REGISTER_BUG        0x02000000L    /*  If set, indicates we are running a PCIE asic with */
-                                                   /*  the FAD/HDP reg access bug.  Bit is read by DAL */
+#define ATOM_S0_FAD_REGISTER_BUG        0x02000000L // If set, indicates we are running a PCIE asic with 
+                                                    // the FAD/HDP reg access bug.  Bit is read by DAL, this is obsolete from RV5xx
 
 #define ATOM_S0_THERMAL_STATE_MASK      0x1C000000L
 #define ATOM_S0_THERMAL_STATE_SHIFT     26
 
 #define ATOM_S0_SYSTEM_POWER_STATE_MASK 0xE0000000L
-#define ATOM_S0_SYSTEM_POWER_STATE_SHIFT 29
+#define ATOM_S0_SYSTEM_POWER_STATE_SHIFT 29 
 
 #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_AC     1
 #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC     2
 #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3
 
-/* Byte aligned definition for BIOS usage */
+//Byte aligned defintion for BIOS usage
 #define ATOM_S0_CRT1_MONOb0             0x01
 #define ATOM_S0_CRT1_COLORb0            0x02
 #define ATOM_S0_CRT1_MASKb0             (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0)
@@ -3076,8 +3934,11 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S0_DFP2b2                  0x02
 #define ATOM_S0_LCD1b2                  0x04
 #define ATOM_S0_LCD2b2                  0x08
-#define ATOM_S0_TV2b2                   0x10
-#define ATOM_S0_DFP3b2                                                                 0x20
+#define ATOM_S0_DFP6b2                  0x10
+#define ATOM_S0_DFP3b2                  0x20
+#define ATOM_S0_DFP4b2                  0x40
+#define ATOM_S0_DFP5b2                  0x80
+
 
 #define ATOM_S0_THERMAL_STATE_MASKb3    0x1C
 #define ATOM_S0_THERMAL_STATE_SHIFTb3   2
@@ -3085,43 +3946,20 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S0_SYSTEM_POWER_STATE_MASKb3 0xE0
 #define ATOM_S0_LCD1_SHIFT              18
 
-/*  BIOS_1_SCRATCH Definition */
+// BIOS_1_SCRATCH Definition
 #define ATOM_S1_ROM_LOCATION_MASK       0x0000FFFFL
 #define ATOM_S1_PCI_BUS_DEV_MASK        0xFFFF0000L
 
-/*       BIOS_2_SCRATCH Definition */
+//     BIOS_2_SCRATCH Definition
 #define ATOM_S2_TV1_STANDARD_MASK       0x0000000FL
 #define ATOM_S2_CURRENT_BL_LEVEL_MASK   0x0000FF00L
 #define ATOM_S2_CURRENT_BL_LEVEL_SHIFT  8
 
-#define ATOM_S2_CRT1_DPMS_STATE         0x00010000L
-#define ATOM_S2_LCD1_DPMS_STATE                0x00020000L
-#define ATOM_S2_TV1_DPMS_STATE          0x00040000L
-#define ATOM_S2_DFP1_DPMS_STATE         0x00080000L
-#define ATOM_S2_CRT2_DPMS_STATE         0x00100000L
-#define ATOM_S2_LCD2_DPMS_STATE         0x00200000L
-#define ATOM_S2_TV2_DPMS_STATE          0x00400000L
-#define ATOM_S2_DFP2_DPMS_STATE         0x00800000L
-#define ATOM_S2_CV_DPMS_STATE           0x01000000L
-#define ATOM_S2_DFP3_DPMS_STATE                                        0x02000000L
-#define ATOM_S2_DFP4_DPMS_STATE                                        0x04000000L
-#define ATOM_S2_DFP5_DPMS_STATE                                        0x08000000L
-
-#define ATOM_S2_DFP_DPM_STATE \
-       (ATOM_S2_DFP1_DPMS_STATE | ATOM_S2_DFP2_DPMS_STATE | \
-        ATOM_S2_DFP3_DPMS_STATE | ATOM_S2_DFP4_DPMS_STATE | \
-        ATOM_S2_DFP5_DPMS_STATE)
-
-#define ATOM_S2_DEVICE_DPMS_STATE \
-       (ATOM_S2_CRT1_DPMS_STATE + ATOM_S2_LCD1_DPMS_STATE + \
-        ATOM_S2_TV1_DPMS_STATE + ATOM_S2_DFP_DPMS_STATE + \
-        ATOM_S2_CRT2_DPMS_STATE + ATOM_S2_LCD2_DPMS_STATE + \
-        ATOM_S2_TV2_DPMS_STATE + ATOM_S2_CV_DPMS_STATE)
-
 #define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK       0x0C000000L
 #define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASK_SHIFT 26
 #define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGE     0x10000000L
 
+#define ATOM_S2_DEVICE_DPMS_STATE       0x00010000L
 #define ATOM_S2_VRI_BRIGHT_ENABLE       0x20000000L
 
 #define ATOM_S2_DISPLAY_ROTATION_0_DEGREE     0x0
@@ -3131,21 +3969,11 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30
 #define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK   0xC0000000L
 
-/* Byte aligned definition for BIOS usage */
+
+//Byte aligned defintion for BIOS usage
 #define ATOM_S2_TV1_STANDARD_MASKb0     0x0F
 #define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF
-#define ATOM_S2_CRT1_DPMS_STATEb2       0x01
-#define ATOM_S2_LCD1_DPMS_STATEb2       0x02
-#define ATOM_S2_TV1_DPMS_STATEb2        0x04
-#define ATOM_S2_DFP1_DPMS_STATEb2       0x08
-#define ATOM_S2_CRT2_DPMS_STATEb2       0x10
-#define ATOM_S2_LCD2_DPMS_STATEb2       0x20
-#define ATOM_S2_TV2_DPMS_STATEb2        0x40
-#define ATOM_S2_DFP2_DPMS_STATEb2       0x80
-#define ATOM_S2_CV_DPMS_STATEb3         0x01
-#define ATOM_S2_DFP3_DPMS_STATEb3                              0x02
-#define ATOM_S2_DFP4_DPMS_STATEb3                              0x04
-#define ATOM_S2_DFP5_DPMS_STATEb3                              0x08
+#define ATOM_S2_DEVICE_DPMS_STATEb2     0x01
 
 #define ATOM_S2_DEVICE_DPMS_MASKw1      0x3FF
 #define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASKb3     0x0C
@@ -3153,21 +3981,22 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S2_VRI_BRIGHT_ENABLEb3     0x20
 #define ATOM_S2_ROTATION_STATE_MASKb3   0xC0
 
-/*  BIOS_3_SCRATCH Definition */
+
+// BIOS_3_SCRATCH Definition
 #define ATOM_S3_CRT1_ACTIVE             0x00000001L
 #define ATOM_S3_LCD1_ACTIVE             0x00000002L
 #define ATOM_S3_TV1_ACTIVE              0x00000004L
 #define ATOM_S3_DFP1_ACTIVE             0x00000008L
 #define ATOM_S3_CRT2_ACTIVE             0x00000010L
 #define ATOM_S3_LCD2_ACTIVE             0x00000020L
-#define ATOM_S3_TV2_ACTIVE              0x00000040L
+#define ATOM_S3_DFP6_ACTIVE             0x00000040L
 #define ATOM_S3_DFP2_ACTIVE             0x00000080L
 #define ATOM_S3_CV_ACTIVE               0x00000100L
 #define ATOM_S3_DFP3_ACTIVE                                                    0x00000200L
 #define ATOM_S3_DFP4_ACTIVE                                                    0x00000400L
 #define ATOM_S3_DFP5_ACTIVE                                                    0x00000800L
 
-#define ATOM_S3_DEVICE_ACTIVE_MASK      0x000003FFL
+#define ATOM_S3_DEVICE_ACTIVE_MASK      0x00000FFFL
 
 #define ATOM_S3_LCD_FULLEXPANSION_ACTIVE         0x00001000L
 #define ATOM_S3_LCD_EXPANSION_ASPEC_RATIO_ACTIVE 0x00002000L
@@ -3178,7 +4007,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S3_DFP1_CRTC_ACTIVE        0x00080000L
 #define ATOM_S3_CRT2_CRTC_ACTIVE        0x00100000L
 #define ATOM_S3_LCD2_CRTC_ACTIVE        0x00200000L
-#define ATOM_S3_TV2_CRTC_ACTIVE         0x00400000L
+#define ATOM_S3_DFP6_CRTC_ACTIVE        0x00400000L
 #define ATOM_S3_DFP2_CRTC_ACTIVE        0x00800000L
 #define ATOM_S3_CV_CRTC_ACTIVE          0x01000000L
 #define ATOM_S3_DFP3_CRTC_ACTIVE                               0x02000000L
@@ -3187,17 +4016,18 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 
 #define ATOM_S3_DEVICE_CRTC_ACTIVE_MASK 0x0FFF0000L
 #define ATOM_S3_ASIC_GUI_ENGINE_HUNG    0x20000000L
+//Below two definitions are not supported in pplib, but in the old powerplay in DAL
 #define ATOM_S3_ALLOW_FAST_PWR_SWITCH   0x40000000L
 #define ATOM_S3_RQST_GPU_USE_MIN_PWR    0x80000000L
 
-/* Byte aligned definition for BIOS usage */
+//Byte aligned defintion for BIOS usage
 #define ATOM_S3_CRT1_ACTIVEb0           0x01
 #define ATOM_S3_LCD1_ACTIVEb0           0x02
 #define ATOM_S3_TV1_ACTIVEb0            0x04
 #define ATOM_S3_DFP1_ACTIVEb0           0x08
 #define ATOM_S3_CRT2_ACTIVEb0           0x10
 #define ATOM_S3_LCD2_ACTIVEb0           0x20
-#define ATOM_S3_TV2_ACTIVEb0            0x40
+#define ATOM_S3_DFP6_ACTIVEb0           0x40
 #define ATOM_S3_DFP2_ACTIVEb0           0x80
 #define ATOM_S3_CV_ACTIVEb1             0x01
 #define ATOM_S3_DFP3_ACTIVEb1                                          0x02
@@ -3212,7 +4042,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S3_DFP1_CRTC_ACTIVEb2      0x08
 #define ATOM_S3_CRT2_CRTC_ACTIVEb2      0x10
 #define ATOM_S3_LCD2_CRTC_ACTIVEb2      0x20
-#define ATOM_S3_TV2_CRTC_ACTIVEb2       0x40
+#define ATOM_S3_DFP6_CRTC_ACTIVEb2      0x40
 #define ATOM_S3_DFP2_CRTC_ACTIVEb2      0x80
 #define ATOM_S3_CV_CRTC_ACTIVEb3        0x01
 #define ATOM_S3_DFP3_CRTC_ACTIVEb3                     0x02
@@ -3221,35 +4051,31 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 
 #define ATOM_S3_ACTIVE_CRTC2w1          0xFFF
 
-#define ATOM_S3_ASIC_GUI_ENGINE_HUNGb3 0x20
-#define ATOM_S3_ALLOW_FAST_PWR_SWITCHb3 0x40
-#define ATOM_S3_RQST_GPU_USE_MIN_PWRb3  0x80
-
-/*  BIOS_4_SCRATCH Definition */
+// BIOS_4_SCRATCH Definition
 #define ATOM_S4_LCD1_PANEL_ID_MASK      0x000000FFL
 #define ATOM_S4_LCD1_REFRESH_MASK       0x0000FF00L
 #define ATOM_S4_LCD1_REFRESH_SHIFT      8
 
-/* Byte aligned definition for BIOS usage */
+//Byte aligned defintion for BIOS usage
 #define ATOM_S4_LCD1_PANEL_ID_MASKb0     0x0FF
 #define ATOM_S4_LCD1_REFRESH_MASKb1              ATOM_S4_LCD1_PANEL_ID_MASKb0
 #define ATOM_S4_VRAM_INFO_MASKb2        ATOM_S4_LCD1_PANEL_ID_MASKb0
 
-/*  BIOS_5_SCRATCH Definition, BIOS_5_SCRATCH is used by Firmware only !!!! */
+// BIOS_5_SCRATCH Definition, BIOS_5_SCRATCH is used by Firmware only !!!!
 #define ATOM_S5_DOS_REQ_CRT1b0          0x01
 #define ATOM_S5_DOS_REQ_LCD1b0          0x02
 #define ATOM_S5_DOS_REQ_TV1b0           0x04
 #define ATOM_S5_DOS_REQ_DFP1b0          0x08
 #define ATOM_S5_DOS_REQ_CRT2b0          0x10
 #define ATOM_S5_DOS_REQ_LCD2b0          0x20
-#define ATOM_S5_DOS_REQ_TV2b0           0x40
+#define ATOM_S5_DOS_REQ_DFP6b0          0x40
 #define ATOM_S5_DOS_REQ_DFP2b0          0x80
 #define ATOM_S5_DOS_REQ_CVb1            0x01
 #define ATOM_S5_DOS_REQ_DFP3b1                                 0x02
 #define ATOM_S5_DOS_REQ_DFP4b1                                 0x04
 #define ATOM_S5_DOS_REQ_DFP5b1                                 0x08
 
-#define ATOM_S5_DOS_REQ_DEVICEw0        0x03FF
+#define ATOM_S5_DOS_REQ_DEVICEw0        0x0FFF
 
 #define ATOM_S5_DOS_REQ_CRT1            0x0001
 #define ATOM_S5_DOS_REQ_LCD1            0x0002
@@ -3257,22 +4083,21 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S5_DOS_REQ_DFP1            0x0008
 #define ATOM_S5_DOS_REQ_CRT2            0x0010
 #define ATOM_S5_DOS_REQ_LCD2            0x0020
-#define ATOM_S5_DOS_REQ_TV2             0x0040
+#define ATOM_S5_DOS_REQ_DFP6            0x0040
 #define ATOM_S5_DOS_REQ_DFP2            0x0080
 #define ATOM_S5_DOS_REQ_CV              0x0100
-#define ATOM_S5_DOS_REQ_DFP3                                           0x0200
-#define ATOM_S5_DOS_REQ_DFP4                                           0x0400
-#define ATOM_S5_DOS_REQ_DFP5                                           0x0800
+#define ATOM_S5_DOS_REQ_DFP3            0x0200
+#define ATOM_S5_DOS_REQ_DFP4            0x0400
+#define ATOM_S5_DOS_REQ_DFP5            0x0800
 
 #define ATOM_S5_DOS_FORCE_CRT1b2        ATOM_S5_DOS_REQ_CRT1b0
 #define ATOM_S5_DOS_FORCE_TV1b2         ATOM_S5_DOS_REQ_TV1b0
 #define ATOM_S5_DOS_FORCE_CRT2b2        ATOM_S5_DOS_REQ_CRT2b0
 #define ATOM_S5_DOS_FORCE_CVb3          ATOM_S5_DOS_REQ_CVb1
-#define ATOM_S5_DOS_FORCE_DEVICEw1 \
-       (ATOM_S5_DOS_FORCE_CRT1b2 + ATOM_S5_DOS_FORCE_TV1b2 + \
-        ATOM_S5_DOS_FORCE_CRT2b2 + (ATOM_S5_DOS_FORCE_CVb3 << 8))
+#define ATOM_S5_DOS_FORCE_DEVICEw1      (ATOM_S5_DOS_FORCE_CRT1b2+ATOM_S5_DOS_FORCE_TV1b2+ATOM_S5_DOS_FORCE_CRT2b2+\
+                                        (ATOM_S5_DOS_FORCE_CVb3<<8))
 
-/*  BIOS_6_SCRATCH Definition */
+// BIOS_6_SCRATCH Definition
 #define ATOM_S6_DEVICE_CHANGE           0x00000001L
 #define ATOM_S6_SCALER_CHANGE           0x00000002L
 #define ATOM_S6_LID_CHANGE              0x00000004L
@@ -3285,11 +4110,11 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_HW_I2C_BUSY_STATE       0x00000200L
 #define ATOM_S6_THERMAL_STATE_CHANGE    0x00000400L
 #define ATOM_S6_INTERRUPT_SET_BY_BIOS   0x00000800L
-#define ATOM_S6_REQ_LCD_EXPANSION_FULL         0x00001000L     /* Normal expansion Request bit for LCD */
-#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO  0x00002000L     /* Aspect ratio expansion Request bit for LCD */
+#define ATOM_S6_REQ_LCD_EXPANSION_FULL         0x00001000L //Normal expansion Request bit for LCD
+#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIO  0x00002000L //Aspect ratio expansion Request bit for LCD
 
-#define ATOM_S6_DISPLAY_STATE_CHANGE    0x00004000L    /* This bit is recycled when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_H_expansion */
-#define ATOM_S6_I2C_STATE_CHANGE        0x00008000L    /* This bit is recycled,when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_V_expansion */
+#define ATOM_S6_DISPLAY_STATE_CHANGE    0x00004000L        //This bit is recycled when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_H_expansion
+#define ATOM_S6_I2C_STATE_CHANGE        0x00008000L        //This bit is recycled,when ATOM_BIOS_INFO_BIOS_SCRATCH6_SCL2_REDEFINE is set,previously it's SCL2_V_expansion
 
 #define ATOM_S6_ACC_REQ_CRT1            0x00010000L
 #define ATOM_S6_ACC_REQ_LCD1            0x00020000L
@@ -3297,7 +4122,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_ACC_REQ_DFP1            0x00080000L
 #define ATOM_S6_ACC_REQ_CRT2            0x00100000L
 #define ATOM_S6_ACC_REQ_LCD2            0x00200000L
-#define ATOM_S6_ACC_REQ_TV2             0x00400000L
+#define ATOM_S6_ACC_REQ_DFP6            0x00400000L
 #define ATOM_S6_ACC_REQ_DFP2            0x00800000L
 #define ATOM_S6_ACC_REQ_CV              0x01000000L
 #define ATOM_S6_ACC_REQ_DFP3                                           0x02000000L
@@ -3310,7 +4135,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_VRI_BRIGHTNESS_CHANGE       0x40000000L
 #define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK  0x80000000L
 
-/* Byte aligned definition for BIOS usage */
+//Byte aligned defintion for BIOS usage
 #define ATOM_S6_DEVICE_CHANGEb0         0x01
 #define ATOM_S6_SCALER_CHANGEb0         0x02
 #define ATOM_S6_LID_CHANGEb0            0x04
@@ -3320,11 +4145,11 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_LID_STATEb0             0x40
 #define ATOM_S6_DOCK_STATEb0            0x80
 #define ATOM_S6_CRITICAL_STATEb1        0x01
-#define ATOM_S6_HW_I2C_BUSY_STATEb1     0x02
+#define ATOM_S6_HW_I2C_BUSY_STATEb1     0x02  
 #define ATOM_S6_THERMAL_STATE_CHANGEb1  0x04
 #define ATOM_S6_INTERRUPT_SET_BY_BIOSb1 0x08
-#define ATOM_S6_REQ_LCD_EXPANSION_FULLb1        0x10
-#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIOb1 0x20
+#define ATOM_S6_REQ_LCD_EXPANSION_FULLb1        0x10    
+#define ATOM_S6_REQ_LCD_EXPANSION_ASPEC_RATIOb1 0x20 
 
 #define ATOM_S6_ACC_REQ_CRT1b2          0x01
 #define ATOM_S6_ACC_REQ_LCD1b2          0x02
@@ -3332,12 +4157,12 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_ACC_REQ_DFP1b2          0x08
 #define ATOM_S6_ACC_REQ_CRT2b2          0x10
 #define ATOM_S6_ACC_REQ_LCD2b2          0x20
-#define ATOM_S6_ACC_REQ_TV2b2           0x40
+#define ATOM_S6_ACC_REQ_DFP6b2          0x40
 #define ATOM_S6_ACC_REQ_DFP2b2          0x80
 #define ATOM_S6_ACC_REQ_CVb3            0x01
-#define ATOM_S6_ACC_REQ_DFP3b3                                 0x02
-#define ATOM_S6_ACC_REQ_DFP4b3                                 0x04
-#define ATOM_S6_ACC_REQ_DFP5b3                                 0x08
+#define ATOM_S6_ACC_REQ_DFP3b3          0x02
+#define ATOM_S6_ACC_REQ_DFP4b3          0x04
+#define ATOM_S6_ACC_REQ_DFP5b3          0x08
 
 #define ATOM_S6_ACC_REQ_DEVICEw1        ATOM_S5_DOS_REQ_DEVICEw0
 #define ATOM_S6_SYSTEM_POWER_MODE_CHANGEb3 0x10
@@ -3366,7 +4191,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT     30
 #define ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT     31
 
-/*  BIOS_7_SCRATCH Definition, BIOS_7_SCRATCH is used by Firmware only !!!! */
+// BIOS_7_SCRATCH Definition, BIOS_7_SCRATCH is used by Firmware only !!!!
 #define ATOM_S7_DOS_MODE_TYPEb0             0x03
 #define ATOM_S7_DOS_MODE_VGAb0              0x00
 #define ATOM_S7_DOS_MODE_VESAb0             0x01
@@ -3378,220 +4203,194 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 
 #define ATOM_S7_DOS_8BIT_DAC_EN_SHIFT       8
 
-/*  BIOS_8_SCRATCH Definition */
+// BIOS_8_SCRATCH Definition
 #define ATOM_S8_I2C_CHANNEL_BUSY_MASK       0x00000FFFF
-#define ATOM_S8_I2C_HW_ENGINE_BUSY_MASK     0x0FFFF0000
+#define ATOM_S8_I2C_HW_ENGINE_BUSY_MASK     0x0FFFF0000   
 
 #define ATOM_S8_I2C_CHANNEL_BUSY_SHIFT      0
 #define ATOM_S8_I2C_ENGINE_BUSY_SHIFT       16
 
-/*  BIOS_9_SCRATCH Definition */
-#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_MASK
+// BIOS_9_SCRATCH Definition
+#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_MASK 
 #define ATOM_S9_I2C_CHANNEL_COMPLETED_MASK  0x0000FFFF
 #endif
-#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_MASK
+#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_MASK  
 #define ATOM_S9_I2C_CHANNEL_ABORTED_MASK    0xFFFF0000
 #endif
-#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT
+#ifndef ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT 
 #define ATOM_S9_I2C_CHANNEL_COMPLETED_SHIFT 0
 #endif
-#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT
+#ifndef ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT   
 #define ATOM_S9_I2C_CHANNEL_ABORTED_SHIFT   16
 #endif
 
 #define ATOM_FLAG_SET                         0x20
 #define ATOM_FLAG_CLEAR                       0
-#define CLEAR_ATOM_S6_ACC_MODE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_ACC_MODE_SHIFT | ATOM_FLAG_CLEAR)
-#define SET_ATOM_S6_DEVICE_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_DEVICE_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define SET_ATOM_S6_VRI_BRIGHTNESS_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define SET_ATOM_S6_SCALER_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_SCALER_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define SET_ATOM_S6_LID_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_LID_CHANGE_SHIFT | ATOM_FLAG_SET)
-
-#define SET_ATOM_S6_LID_STATE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) |\
-        ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_SET)
-#define CLEAR_ATOM_S6_LID_STATE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_CLEAR)
-
-#define SET_ATOM_S6_DOCK_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8)| \
-        ATOM_S6_DOCKING_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define SET_ATOM_S6_DOCK_STATE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_SET)
-#define CLEAR_ATOM_S6_DOCK_STATE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_CLEAR)
-
-#define SET_ATOM_S6_THERMAL_STATE_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_THERMAL_STATE_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define SET_ATOM_S6_SYSTEM_POWER_MODE_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define SET_ATOM_S6_INTERRUPT_SET_BY_BIOS \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT | ATOM_FLAG_SET)
-
-#define SET_ATOM_S6_CRITICAL_STATE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_SET)
-#define CLEAR_ATOM_S6_CRITICAL_STATE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_CLEAR)
-
-#define SET_ATOM_S6_REQ_SCALER \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_SET)
-#define CLEAR_ATOM_S6_REQ_SCALER \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_CLEAR )
-
-#define SET_ATOM_S6_REQ_SCALER_ARATIO \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_SET )
-#define CLEAR_ATOM_S6_REQ_SCALER_ARATIO \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_CLEAR )
-
-#define SET_ATOM_S6_I2C_STATE_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_I2C_STATE_CHANGE_SHIFT | ATOM_FLAG_SET )
-
-#define SET_ATOM_S6_DISPLAY_STATE_CHANGE \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT | ATOM_FLAG_SET )
-
-#define SET_ATOM_S6_DEVICE_RECONFIG \
-       ((ATOM_ACC_CHANGE_INFO_DEF << 8) | \
-        ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT | ATOM_FLAG_SET)
-#define CLEAR_ATOM_S0_LCD1 \
-       ((ATOM_DEVICE_CONNECT_INFO_DEF << 8 ) | \
-        ATOM_S0_LCD1_SHIFT | ATOM_FLAG_CLEAR )
-#define SET_ATOM_S7_DOS_8BIT_DAC_EN \
-       ((ATOM_DOS_MODE_INFO_DEF << 8) | \
-        ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_SET )
-#define CLEAR_ATOM_S7_DOS_8BIT_DAC_EN \
-       ((ATOM_DOS_MODE_INFO_DEF << 8) | \
-        ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_CLEAR )
+#define CLEAR_ATOM_S6_ACC_MODE                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_ACC_MODE_SHIFT | ATOM_FLAG_CLEAR)
+#define SET_ATOM_S6_DEVICE_CHANGE             ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DEVICE_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define SET_ATOM_S6_VRI_BRIGHTNESS_CHANGE     ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_VRI_BRIGHTNESS_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define SET_ATOM_S6_SCALER_CHANGE             ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SCALER_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define SET_ATOM_S6_LID_CHANGE                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_CHANGE_SHIFT | ATOM_FLAG_SET)
 
-/****************************************************************************/
-/* Portion II: Definitinos only used in Driver */
+#define SET_ATOM_S6_LID_STATE                 ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_SET)
+#define CLEAR_ATOM_S6_LID_STATE               ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_LID_STATE_SHIFT | ATOM_FLAG_CLEAR)
+
+#define SET_ATOM_S6_DOCK_CHANGE                                  ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCKING_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define SET_ATOM_S6_DOCK_STATE                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_SET)
+#define CLEAR_ATOM_S6_DOCK_STATE              ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DOCK_STATE_SHIFT | ATOM_FLAG_CLEAR)
+
+#define SET_ATOM_S6_THERMAL_STATE_CHANGE      ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_THERMAL_STATE_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define SET_ATOM_S6_SYSTEM_POWER_MODE_CHANGE  ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_SYSTEM_POWER_MODE_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define SET_ATOM_S6_INTERRUPT_SET_BY_BIOS     ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_INTERRUPT_SET_BY_BIOS_SHIFT | ATOM_FLAG_SET)
+
+#define SET_ATOM_S6_CRITICAL_STATE            ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_SET)
+#define CLEAR_ATOM_S6_CRITICAL_STATE          ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CRITICAL_STATE_SHIFT | ATOM_FLAG_CLEAR)
+
+#define SET_ATOM_S6_REQ_SCALER                ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_SET)  
+#define CLEAR_ATOM_S6_REQ_SCALER              ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_SHIFT | ATOM_FLAG_CLEAR )
+
+#define SET_ATOM_S6_REQ_SCALER_ARATIO         ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_SET )
+#define CLEAR_ATOM_S6_REQ_SCALER_ARATIO       ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_REQ_SCALER_ARATIO_SHIFT | ATOM_FLAG_CLEAR )
+
+#define SET_ATOM_S6_I2C_STATE_CHANGE          ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_I2C_STATE_CHANGE_SHIFT | ATOM_FLAG_SET )
+
+#define SET_ATOM_S6_DISPLAY_STATE_CHANGE      ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_DISPLAY_STATE_CHANGE_SHIFT | ATOM_FLAG_SET )
+
+#define SET_ATOM_S6_DEVICE_RECONFIG           ((ATOM_ACC_CHANGE_INFO_DEF << 8 )|ATOM_S6_CONFIG_DISPLAY_CHANGE_SHIFT | ATOM_FLAG_SET)
+#define CLEAR_ATOM_S0_LCD1                    ((ATOM_DEVICE_CONNECT_INFO_DEF << 8 )|  ATOM_S0_LCD1_SHIFT | ATOM_FLAG_CLEAR )
+#define SET_ATOM_S7_DOS_8BIT_DAC_EN           ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_SET )
+#define CLEAR_ATOM_S7_DOS_8BIT_DAC_EN         ((ATOM_DOS_MODE_INFO_DEF << 8 )|ATOM_S7_DOS_8BIT_DAC_EN_SHIFT | ATOM_FLAG_CLEAR )
+
+/****************************************************************************/ 
+//Portion II: Definitinos only used in Driver
 /****************************************************************************/
 
-/*  Macros used by driver */
+// Macros used by driver
+#ifdef __cplusplus
+#define GetIndexIntoMasterTable(MasterOrData, FieldName) ((reinterpret_cast<char*>(&(static_cast<ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES*>(0))->FieldName)-static_cast<char*>(0))/sizeof(USHORT))
 
-#define        GetIndexIntoMasterTable(MasterOrData, FieldName) (((char *)(&((ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES *)0)->FieldName)-(char *)0)/sizeof(USHORT))
+#define GET_COMMAND_TABLE_COMMANDSET_REVISION(TABLE_HEADER_OFFSET) (((static_cast<ATOM_COMMON_TABLE_HEADER*>(TABLE_HEADER_OFFSET))->ucTableFormatRevision )&0x3F)
+#define GET_COMMAND_TABLE_PARAMETER_REVISION(TABLE_HEADER_OFFSET)  (((static_cast<ATOM_COMMON_TABLE_HEADER*>(TABLE_HEADER_OFFSET))->ucTableContentRevision)&0x3F)
+#else // not __cplusplus
+#define        GetIndexIntoMasterTable(MasterOrData, FieldName) (((char*)(&((ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES*)0)->FieldName)-(char*)0)/sizeof(USHORT))
 
 #define GET_COMMAND_TABLE_COMMANDSET_REVISION(TABLE_HEADER_OFFSET) ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableFormatRevision)&0x3F)
 #define GET_COMMAND_TABLE_PARAMETER_REVISION(TABLE_HEADER_OFFSET)  ((((ATOM_COMMON_TABLE_HEADER*)TABLE_HEADER_OFFSET)->ucTableContentRevision)&0x3F)
+#endif // __cplusplus
 
 #define GET_DATA_TABLE_MAJOR_REVISION GET_COMMAND_TABLE_COMMANDSET_REVISION
 #define GET_DATA_TABLE_MINOR_REVISION GET_COMMAND_TABLE_PARAMETER_REVISION
 
-/****************************************************************************/
-/* Portion III: Definitinos only used in VBIOS */
+/****************************************************************************/ 
+//Portion III: Definitinos only used in VBIOS
 /****************************************************************************/
 #define ATOM_DAC_SRC                                   0x80
 #define ATOM_SRC_DAC1                                  0
 #define ATOM_SRC_DAC2                                  0x80
 
-#ifdef UEFI_BUILD
-#define        USHORT  UTEMP
-#endif
-
-typedef struct _MEMORY_PLLINIT_PARAMETERS {
-       ULONG ulTargetMemoryClock;      /* In 10Khz unit */
-       UCHAR ucAction;         /* not define yet */
-       UCHAR ucFbDiv_Hi;       /* Fbdiv Hi byte */
-       UCHAR ucFbDiv;          /* FB value */
-       UCHAR ucPostDiv;        /* Post div */
-} MEMORY_PLLINIT_PARAMETERS;
+typedef struct _MEMORY_PLLINIT_PARAMETERS
+{
+  ULONG ulTargetMemoryClock; //In 10Khz unit
+  UCHAR   ucAction;                                     //not define yet
+  UCHAR   ucFbDiv_Hi;                           //Fbdiv Hi byte
+  UCHAR   ucFbDiv;                                      //FB value
+  UCHAR   ucPostDiv;                            //Post div
+}MEMORY_PLLINIT_PARAMETERS;
 
 #define MEMORY_PLLINIT_PS_ALLOCATION  MEMORY_PLLINIT_PARAMETERS
 
-#define        GPIO_PIN_WRITE                                                                                                  0x01
+
+#define        GPIO_PIN_WRITE                                                                                                  0x01                    
 #define        GPIO_PIN_READ                                                                                                           0x00
 
-typedef struct _GPIO_PIN_CONTROL_PARAMETERS {
-       UCHAR ucGPIO_ID;        /* return value, read from GPIO pins */
-       UCHAR ucGPIOBitShift;   /* define which bit in uGPIOBitVal need to be update */
-       UCHAR ucGPIOBitVal;     /* Set/Reset corresponding bit defined in ucGPIOBitMask */
-       UCHAR ucAction;         /* =GPIO_PIN_WRITE: Read; =GPIO_PIN_READ: Write */
-} GPIO_PIN_CONTROL_PARAMETERS;
-
-typedef struct _ENABLE_SCALER_PARAMETERS {
-       UCHAR ucScaler;         /*  ATOM_SCALER1, ATOM_SCALER2 */
-       UCHAR ucEnable;         /*  ATOM_SCALER_DISABLE or ATOM_SCALER_CENTER or ATOM_SCALER_EXPANSION */
-       UCHAR ucTVStandard;     /*  */
-       UCHAR ucPadding[1];
-} ENABLE_SCALER_PARAMETERS;
-#define ENABLE_SCALER_PS_ALLOCATION ENABLE_SCALER_PARAMETERS
-
-/* ucEnable: */
+typedef struct  _GPIO_PIN_CONTROL_PARAMETERS
+{
+  UCHAR ucGPIO_ID;           //return value, read from GPIO pins
+  UCHAR ucGPIOBitShift;             //define which bit in uGPIOBitVal need to be update 
+       UCHAR ucGPIOBitVal;                  //Set/Reset corresponding bit defined in ucGPIOBitMask
+  UCHAR ucAction;                                   //=GPIO_PIN_WRITE: Read; =GPIO_PIN_READ: Write
+}GPIO_PIN_CONTROL_PARAMETERS;
+
+typedef struct _ENABLE_SCALER_PARAMETERS
+{
+  UCHAR ucScaler;            // ATOM_SCALER1, ATOM_SCALER2
+  UCHAR ucEnable;            // ATOM_SCALER_DISABLE or ATOM_SCALER_CENTER or ATOM_SCALER_EXPANSION
+  UCHAR ucTVStandard;        // 
+  UCHAR ucPadding[1];
+}ENABLE_SCALER_PARAMETERS; 
+#define ENABLE_SCALER_PS_ALLOCATION ENABLE_SCALER_PARAMETERS 
+
+//ucEnable:
 #define SCALER_BYPASS_AUTO_CENTER_NO_REPLICATION    0
 #define SCALER_BYPASS_AUTO_CENTER_AUTO_REPLICATION  1
 #define SCALER_ENABLE_2TAP_ALPHA_MODE               2
 #define SCALER_ENABLE_MULTITAP_MODE                 3
 
-typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS {
-       ULONG usHWIconHorzVertPosn;     /*  Hardware Icon Vertical position */
-       UCHAR ucHWIconVertOffset;       /*  Hardware Icon Vertical offset */
-       UCHAR ucHWIconHorzOffset;       /*  Hardware Icon Horizontal offset */
-       UCHAR ucSelection;      /*  ATOM_CURSOR1 or ATOM_ICON1 or ATOM_CURSOR2 or ATOM_ICON2 */
-       UCHAR ucEnable;         /*  ATOM_ENABLE or ATOM_DISABLE */
-} ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS;
-
-typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION {
-       ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS sEnableIcon;
-       ENABLE_CRTC_PARAMETERS sReserved;
-} ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION;
-
-typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS {
-       USHORT usHight;         /*  Image Hight */
-       USHORT usWidth;         /*  Image Width */
-       UCHAR ucSurface;        /*  Surface 1 or 2 */
-       UCHAR ucPadding[3];
-} ENABLE_GRAPH_SURFACE_PARAMETERS;
-
-typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2 {
-       USHORT usHight;         /*  Image Hight */
-       USHORT usWidth;         /*  Image Width */
-       UCHAR ucSurface;        /*  Surface 1 or 2 */
-       UCHAR ucEnable;         /*  ATOM_ENABLE or ATOM_DISABLE */
-       UCHAR ucPadding[2];
-} ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2;
-
-typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION {
-       ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface;
-       ENABLE_YUV_PS_ALLOCATION sReserved;     /*  Don't set this one */
-} ENABLE_GRAPH_SURFACE_PS_ALLOCATION;
-
-typedef struct _MEMORY_CLEAN_UP_PARAMETERS {
-       USHORT usMemoryStart;   /* in 8Kb boundry, offset from memory base address */
-       USHORT usMemorySize;    /* 8Kb blocks aligned */
-} MEMORY_CLEAN_UP_PARAMETERS;
+typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS
+{
+  ULONG  usHWIconHorzVertPosn;        // Hardware Icon Vertical position
+  UCHAR  ucHWIconVertOffset;          // Hardware Icon Vertical offset
+  UCHAR  ucHWIconHorzOffset;          // Hardware Icon Horizontal offset
+  UCHAR  ucSelection;                 // ATOM_CURSOR1 or ATOM_ICON1 or ATOM_CURSOR2 or ATOM_ICON2
+  UCHAR  ucEnable;                    // ATOM_ENABLE or ATOM_DISABLE
+}ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS;
+
+typedef struct _ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION
+{
+  ENABLE_HARDWARE_ICON_CURSOR_PARAMETERS  sEnableIcon;
+  ENABLE_CRTC_PARAMETERS                  sReserved;  
+}ENABLE_HARDWARE_ICON_CURSOR_PS_ALLOCATION;
+
+typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS
+{
+  USHORT usHight;                     // Image Hight
+  USHORT usWidth;                     // Image Width
+  UCHAR  ucSurface;                   // Surface 1 or 2        
+  UCHAR  ucPadding[3];
+}ENABLE_GRAPH_SURFACE_PARAMETERS;
+
+typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2
+{
+  USHORT usHight;                     // Image Hight
+  USHORT usWidth;                     // Image Width
+  UCHAR  ucSurface;                   // Surface 1 or 2
+  UCHAR  ucEnable;                    // ATOM_ENABLE or ATOM_DISABLE
+  UCHAR  ucPadding[2];
+}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_2;
+
+typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3
+{
+  USHORT usHight;                     // Image Hight
+  USHORT usWidth;                     // Image Width
+  UCHAR  ucSurface;                   // Surface 1 or 2
+  UCHAR  ucEnable;                    // ATOM_ENABLE or ATOM_DISABLE
+  USHORT usDeviceId;                  // Active Device Id for this surface. If no device, set to 0. 
+}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3;
+
+typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION
+{
+  ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface;          
+  ENABLE_YUV_PS_ALLOCATION        sReserved; // Don't set this one
+}ENABLE_GRAPH_SURFACE_PS_ALLOCATION;
+
+typedef struct _MEMORY_CLEAN_UP_PARAMETERS
+{
+  USHORT  usMemoryStart;                //in 8Kb boundry, offset from memory base address
+  USHORT  usMemorySize;                 //8Kb blocks aligned
+}MEMORY_CLEAN_UP_PARAMETERS;
 #define MEMORY_CLEAN_UP_PS_ALLOCATION MEMORY_CLEAN_UP_PARAMETERS
 
-typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS {
-       USHORT usX_Size;        /* When use as input parameter, usX_Size indicates which CRTC */
-       USHORT usY_Size;
-} GET_DISPLAY_SURFACE_SIZE_PARAMETERS;
+typedef struct  _GET_DISPLAY_SURFACE_SIZE_PARAMETERS
+{
+  USHORT  usX_Size;                     //When use as input parameter, usX_Size indicates which CRTC                 
+  USHORT  usY_Size;
+}GET_DISPLAY_SURFACE_SIZE_PARAMETERS; 
 
-typedef struct _INDIRECT_IO_ACCESS {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR IOAccessSequence[256];
+typedef struct _INDIRECT_IO_ACCESS
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  UCHAR                    IOAccessSequence[256];
 } INDIRECT_IO_ACCESS;
 
 #define INDIRECT_READ              0x00
@@ -3615,93 +4414,108 @@ typedef struct _INDIRECT_IO_ACCESS {
 #define INDIRECT_IO_NBMISC_READ    INDIRECT_IO_NBMISC | INDIRECT_READ
 #define INDIRECT_IO_NBMISC_WRITE   INDIRECT_IO_NBMISC | INDIRECT_WRITE
 
-typedef struct _ATOM_OEM_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;
-} ATOM_OEM_INFO;
-
-typedef struct _ATOM_TV_MODE {
-       UCHAR ucVMode_Num;      /* Video mode number */
-       UCHAR ucTV_Mode_Num;    /* Internal TV mode number */
-} ATOM_TV_MODE;
-
-typedef struct _ATOM_BIOS_INT_TVSTD_MODE {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usTV_Mode_LUT_Offset;    /*  Pointer to standard to internal number conversion table */
-       USHORT usTV_FIFO_Offset;        /*  Pointer to FIFO entry table */
-       USHORT usNTSC_Tbl_Offset;       /*  Pointer to SDTV_Mode_NTSC table */
-       USHORT usPAL_Tbl_Offset;        /*  Pointer to SDTV_Mode_PAL table */
-       USHORT usCV_Tbl_Offset; /*  Pointer to SDTV_Mode_PAL table */
-} ATOM_BIOS_INT_TVSTD_MODE;
-
-typedef struct _ATOM_TV_MODE_SCALER_PTR {
-       USHORT ucFilter0_Offset;        /* Pointer to filter format 0 coefficients */
-       USHORT usFilter1_Offset;        /* Pointer to filter format 0 coefficients */
-       UCHAR ucTV_Mode_Num;
-} ATOM_TV_MODE_SCALER_PTR;
-
-typedef struct _ATOM_STANDARD_VESA_TIMING {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_DTD_FORMAT aModeTimings[16];       /*  16 is not the real array number, just for initial allocation */
-} ATOM_STANDARD_VESA_TIMING;
-
-typedef struct _ATOM_STD_FORMAT {
-       USHORT usSTD_HDisp;
-       USHORT usSTD_VDisp;
-       USHORT usSTD_RefreshRate;
-       USHORT usReserved;
-} ATOM_STD_FORMAT;
-
-typedef struct _ATOM_VESA_TO_EXTENDED_MODE {
-       USHORT usVESA_ModeNumber;
-       USHORT usExtendedModeNumber;
-} ATOM_VESA_TO_EXTENDED_MODE;
-
-typedef struct _ATOM_VESA_TO_INTENAL_MODE_LUT {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       ATOM_VESA_TO_EXTENDED_MODE asVESA_ToExtendedModeInfo[76];
-} ATOM_VESA_TO_INTENAL_MODE_LUT;
+typedef struct _ATOM_OEM_INFO
+{ 
+  ATOM_COMMON_TABLE_HEADER     sHeader;
+  ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;
+}ATOM_OEM_INFO;
+
+typedef struct _ATOM_TV_MODE
+{
+   UCHAR       ucVMode_Num;                      //Video mode number
+   UCHAR       ucTV_Mode_Num;                  //Internal TV mode number
+}ATOM_TV_MODE;
+
+typedef struct _ATOM_BIOS_INT_TVSTD_MODE
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+   USHORT      usTV_Mode_LUT_Offset;   // Pointer to standard to internal number conversion table
+   USHORT      usTV_FIFO_Offset;                 // Pointer to FIFO entry table
+   USHORT      usNTSC_Tbl_Offset;              // Pointer to SDTV_Mode_NTSC table
+   USHORT      usPAL_Tbl_Offset;                 // Pointer to SDTV_Mode_PAL table 
+   USHORT      usCV_Tbl_Offset;                  // Pointer to SDTV_Mode_PAL table 
+}ATOM_BIOS_INT_TVSTD_MODE;
+
+
+typedef struct _ATOM_TV_MODE_SCALER_PTR
+{
+   USHORT      ucFilter0_Offset;               //Pointer to filter format 0 coefficients
+   USHORT      usFilter1_Offset;               //Pointer to filter format 0 coefficients
+   UCHAR       ucTV_Mode_Num;
+}ATOM_TV_MODE_SCALER_PTR;
+
+typedef struct _ATOM_STANDARD_VESA_TIMING
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  ATOM_DTD_FORMAT                               aModeTimings[16];      // 16 is not the real array number, just for initial allocation
+}ATOM_STANDARD_VESA_TIMING;
+
+
+typedef struct _ATOM_STD_FORMAT
+{ 
+  USHORT    usSTD_HDisp;
+  USHORT    usSTD_VDisp;
+  USHORT    usSTD_RefreshRate;
+  USHORT    usReserved;
+}ATOM_STD_FORMAT;
+
+typedef struct _ATOM_VESA_TO_EXTENDED_MODE
+{
+  USHORT  usVESA_ModeNumber;
+  USHORT  usExtendedModeNumber;
+}ATOM_VESA_TO_EXTENDED_MODE;
+
+typedef struct _ATOM_VESA_TO_INTENAL_MODE_LUT
+{ 
+  ATOM_COMMON_TABLE_HEADER   sHeader;  
+  ATOM_VESA_TO_EXTENDED_MODE asVESA_ToExtendedModeInfo[76];
+}ATOM_VESA_TO_INTENAL_MODE_LUT;
 
 /*************** ATOM Memory Related Data Structure ***********************/
-typedef struct _ATOM_MEMORY_VENDOR_BLOCK {
-       UCHAR ucMemoryType;
-       UCHAR ucMemoryVendor;
-       UCHAR ucAdjMCId;
-       UCHAR ucDynClkId;
-       ULONG ulDllResetClkRange;
-} ATOM_MEMORY_VENDOR_BLOCK;
-
-typedef struct _ATOM_MEMORY_SETTING_ID_CONFIG {
+typedef struct _ATOM_MEMORY_VENDOR_BLOCK{
+       UCHAR                                                                                           ucMemoryType;
+       UCHAR                                                                                           ucMemoryVendor;
+       UCHAR                                                                                           ucAdjMCId;
+       UCHAR                                                                                           ucDynClkId;
+       ULONG                                                                                           ulDllResetClkRange;
+}ATOM_MEMORY_VENDOR_BLOCK;
+
+
+typedef struct _ATOM_MEMORY_SETTING_ID_CONFIG{
 #if ATOM_BIG_ENDIAN
-       ULONG ucMemBlkId:8;
-       ULONG ulMemClockRange:24;
+       ULONG                                                                                           ucMemBlkId:8;
+       ULONG                                                                                           ulMemClockRange:24;
 #else
-       ULONG ulMemClockRange:24;
-       ULONG ucMemBlkId:8;
+       ULONG                                                                                           ulMemClockRange:24;
+       ULONG                                                                                           ucMemBlkId:8;
 #endif
-} ATOM_MEMORY_SETTING_ID_CONFIG;
-
-typedef union _ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS {
-       ATOM_MEMORY_SETTING_ID_CONFIG slAccess;
-       ULONG ulAccess;
-} ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS;
-
-typedef struct _ATOM_MEMORY_SETTING_DATA_BLOCK {
-       ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS ulMemoryID;
-       ULONG aulMemData[1];
-} ATOM_MEMORY_SETTING_DATA_BLOCK;
-
-typedef struct _ATOM_INIT_REG_INDEX_FORMAT {
-       USHORT usRegIndex;      /*  MC register index */
-       UCHAR ucPreRegDataLength;       /*  offset in ATOM_INIT_REG_DATA_BLOCK.saRegDataBuf */
-} ATOM_INIT_REG_INDEX_FORMAT;
-
-typedef struct _ATOM_INIT_REG_BLOCK {
-       USHORT usRegIndexTblSize;       /* size of asRegIndexBuf */
-       USHORT usRegDataBlkSize;        /* size of ATOM_MEMORY_SETTING_DATA_BLOCK */
-       ATOM_INIT_REG_INDEX_FORMAT asRegIndexBuf[1];
-       ATOM_MEMORY_SETTING_DATA_BLOCK asRegDataBuf[1];
-} ATOM_INIT_REG_BLOCK;
+}ATOM_MEMORY_SETTING_ID_CONFIG;
+
+typedef union _ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS
+{
+  ATOM_MEMORY_SETTING_ID_CONFIG slAccess;
+  ULONG                         ulAccess;
+}ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS;
+
+
+typedef struct _ATOM_MEMORY_SETTING_DATA_BLOCK{
+       ATOM_MEMORY_SETTING_ID_CONFIG_ACCESS                    ulMemoryID;
+       ULONG                                                                                                                           aulMemData[1];
+}ATOM_MEMORY_SETTING_DATA_BLOCK;
+
+
+typedef struct _ATOM_INIT_REG_INDEX_FORMAT{
+        USHORT                                                                                 usRegIndex;                                     // MC register index
+        UCHAR                                                                                  ucPreRegDataLength;                             // offset in ATOM_INIT_REG_DATA_BLOCK.saRegDataBuf
+}ATOM_INIT_REG_INDEX_FORMAT;
+
+
+typedef struct _ATOM_INIT_REG_BLOCK{
+       USHORT                                                                                                  usRegIndexTblSize;                                                                                                      //size of asRegIndexBuf
+       USHORT                                                                                                  usRegDataBlkSize;                                                                                                               //size of ATOM_MEMORY_SETTING_DATA_BLOCK
+       ATOM_INIT_REG_INDEX_FORMAT                      asRegIndexBuf[1];
+       ATOM_MEMORY_SETTING_DATA_BLOCK  asRegDataBuf[1];
+}ATOM_INIT_REG_BLOCK;
 
 #define END_OF_REG_INDEX_BLOCK  0x0ffff
 #define END_OF_REG_DATA_BLOCK   0x00000000
@@ -3716,16 +4530,19 @@ typedef struct _ATOM_INIT_REG_BLOCK {
 #define INDEX_ACCESS_RANGE_END             (INDEX_ACCESS_RANGE_BEGIN + 1)
 #define VALUE_INDEX_ACCESS_SINGLE          (INDEX_ACCESS_RANGE_END + 1)
 
-typedef struct _ATOM_MC_INIT_PARAM_TABLE {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usAdjustARB_SEQDataOffset;
-       USHORT usMCInitMemTypeTblOffset;
-       USHORT usMCInitCommonTblOffset;
-       USHORT usMCInitPowerDownTblOffset;
-       ULONG ulARB_SEQDataBuf[32];
-       ATOM_INIT_REG_BLOCK asMCInitMemType;
-       ATOM_INIT_REG_BLOCK asMCInitCommon;
-} ATOM_MC_INIT_PARAM_TABLE;
+
+typedef struct _ATOM_MC_INIT_PARAM_TABLE
+{ 
+  ATOM_COMMON_TABLE_HEADER             sHeader;
+  USHORT                                                                                       usAdjustARB_SEQDataOffset;
+  USHORT                                                                                       usMCInitMemTypeTblOffset;
+  USHORT                                                                                       usMCInitCommonTblOffset;
+  USHORT                                                                                       usMCInitPowerDownTblOffset;
+       ULONG                                                                                           ulARB_SEQDataBuf[32];
+       ATOM_INIT_REG_BLOCK                                     asMCInitMemType;
+       ATOM_INIT_REG_BLOCK                                     asMCInitCommon;
+}ATOM_MC_INIT_PARAM_TABLE;
+
 
 #define _4Mx16              0x2
 #define _4Mx32              0x3
@@ -3751,221 +4568,272 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE {
 
 #define QIMONDA             INFINEON
 #define PROMOS              MOSEL
+#define KRETON              INFINEON
 
-/* ///////////Support for GDDR5 MC uCode to reside in upper 64K of ROM///////////// */
+/////////////Support for GDDR5 MC uCode to reside in upper 64K of ROM/////////////
 
 #define UCODE_ROM_START_ADDRESS                0x1c000
-#define        UCODE_SIGNATURE                 0x4375434d      /*  'MCuC' - MC uCode */
-
-/* uCode block header for reference */
-
-typedef struct _MCuCodeHeader {
-       ULONG ulSignature;
-       UCHAR ucRevision;
-       UCHAR ucChecksum;
-       UCHAR ucReserved1;
-       UCHAR ucReserved2;
-       USHORT usParametersLength;
-       USHORT usUCodeLength;
-       USHORT usReserved1;
-       USHORT usReserved2;
+#define        UCODE_SIGNATURE                 0x4375434d // 'MCuC' - MC uCode
+
+//uCode block header for reference
+
+typedef struct _MCuCodeHeader
+{
+  ULONG  ulSignature;
+  UCHAR  ucRevision;
+  UCHAR  ucChecksum;
+  UCHAR  ucReserved1;
+  UCHAR  ucReserved2;
+  USHORT usParametersLength;
+  USHORT usUCodeLength;
+  USHORT usReserved1;
+  USHORT usReserved2;
 } MCuCodeHeader;
 
-/* //////////////////////////////////////////////////////////////////////////////// */
+//////////////////////////////////////////////////////////////////////////////////
 
 #define ATOM_MAX_NUMBER_OF_VRAM_MODULE 16
 
 #define ATOM_VRAM_MODULE_MEMORY_VENDOR_ID_MASK 0xF
-typedef struct _ATOM_VRAM_MODULE_V1 {
-       ULONG ulReserved;
-       USHORT usEMRSValue;
-       USHORT usMRSValue;
-       USHORT usReserved;
-       UCHAR ucExtMemoryID;    /*  An external indicator (by hardcode, callback or pin) to tell what is the current memory module */
-       UCHAR ucMemoryType;     /*  [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] reserved; */
-       UCHAR ucMemoryVenderID; /*  Predefined,never change across designs or memory type/vender */
-       UCHAR ucMemoryDeviceCfg;        /*  [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... */
-       UCHAR ucRow;            /*  Number of Row,in power of 2; */
-       UCHAR ucColumn;         /*  Number of Column,in power of 2; */
-       UCHAR ucBank;           /*  Nunber of Bank; */
-       UCHAR ucRank;           /*  Number of Rank, in power of 2 */
-       UCHAR ucChannelNum;     /*  Number of channel; */
-       UCHAR ucChannelConfig;  /*  [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 */
-       UCHAR ucDefaultMVDDQ_ID;        /*  Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; */
-       UCHAR ucDefaultMVDDC_ID;        /*  Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; */
-       UCHAR ucReserved[2];
-} ATOM_VRAM_MODULE_V1;
-
-typedef struct _ATOM_VRAM_MODULE_V2 {
-       ULONG ulReserved;
-       ULONG ulFlags;          /*  To enable/disable functionalities based on memory type */
-       ULONG ulEngineClock;    /*  Override of default engine clock for particular memory type */
-       ULONG ulMemoryClock;    /*  Override of default memory clock for particular memory type */
-       USHORT usEMRS2Value;    /*  EMRS2 Value is used for GDDR2 and GDDR4 memory type */
-       USHORT usEMRS3Value;    /*  EMRS3 Value is used for GDDR2 and GDDR4 memory type */
-       USHORT usEMRSValue;
-       USHORT usMRSValue;
-       USHORT usReserved;
-       UCHAR ucExtMemoryID;    /*  An external indicator (by hardcode, callback or pin) to tell what is the current memory module */
-       UCHAR ucMemoryType;     /*  [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; */
-       UCHAR ucMemoryVenderID; /*  Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed */
-       UCHAR ucMemoryDeviceCfg;        /*  [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32... */
-       UCHAR ucRow;            /*  Number of Row,in power of 2; */
-       UCHAR ucColumn;         /*  Number of Column,in power of 2; */
-       UCHAR ucBank;           /*  Nunber of Bank; */
-       UCHAR ucRank;           /*  Number of Rank, in power of 2 */
-       UCHAR ucChannelNum;     /*  Number of channel; */
-       UCHAR ucChannelConfig;  /*  [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2 */
-       UCHAR ucDefaultMVDDQ_ID;        /*  Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data; */
-       UCHAR ucDefaultMVDDC_ID;        /*  Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data; */
-       UCHAR ucRefreshRateFactor;
-       UCHAR ucReserved[3];
-} ATOM_VRAM_MODULE_V2;
-
-typedef struct _ATOM_MEMORY_TIMING_FORMAT {
-       ULONG ulClkRange;       /*  memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing */
-       union {
-               USHORT usMRS;   /*  mode register */
-               USHORT usDDR3_MR0;
-       };
-       union {
-               USHORT usEMRS;  /*  extended mode register */
-               USHORT usDDR3_MR1;
-       };
-       UCHAR ucCL;             /*  CAS latency */
-       UCHAR ucWL;             /*  WRITE Latency */
-       UCHAR uctRAS;           /*  tRAS */
-       UCHAR uctRC;            /*  tRC */
-       UCHAR uctRFC;           /*  tRFC */
-       UCHAR uctRCDR;          /*  tRCDR */
-       UCHAR uctRCDW;          /*  tRCDW */
-       UCHAR uctRP;            /*  tRP */
-       UCHAR uctRRD;           /*  tRRD */
-       UCHAR uctWR;            /*  tWR */
-       UCHAR uctWTR;           /*  tWTR */
-       UCHAR uctPDIX;          /*  tPDIX */
-       UCHAR uctFAW;           /*  tFAW */
-       UCHAR uctAOND;          /*  tAOND */
-       union {
-               struct {
-                       UCHAR ucflag;   /*  flag to control memory timing calculation. bit0= control EMRS2 Infineon */
-                       UCHAR ucReserved;
-               };
-               USHORT usDDR3_MR2;
-       };
-} ATOM_MEMORY_TIMING_FORMAT;
-
-typedef struct _ATOM_MEMORY_TIMING_FORMAT_V1 {
-       ULONG ulClkRange;       /*  memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing */
-       USHORT usMRS;           /*  mode register */
-       USHORT usEMRS;          /*  extended mode register */
-       UCHAR ucCL;             /*  CAS latency */
-       UCHAR ucWL;             /*  WRITE Latency */
-       UCHAR uctRAS;           /*  tRAS */
-       UCHAR uctRC;            /*  tRC */
-       UCHAR uctRFC;           /*  tRFC */
-       UCHAR uctRCDR;          /*  tRCDR */
-       UCHAR uctRCDW;          /*  tRCDW */
-       UCHAR uctRP;            /*  tRP */
-       UCHAR uctRRD;           /*  tRRD */
-       UCHAR uctWR;            /*  tWR */
-       UCHAR uctWTR;           /*  tWTR */
-       UCHAR uctPDIX;          /*  tPDIX */
-       UCHAR uctFAW;           /*  tFAW */
-       UCHAR uctAOND;          /*  tAOND */
-       UCHAR ucflag;           /*  flag to control memory timing calculation. bit0= control EMRS2 Infineon */
-/* ///////////////////////GDDR parameters/////////////////////////////////// */
-       UCHAR uctCCDL;          /*  */
-       UCHAR uctCRCRL;         /*  */
-       UCHAR uctCRCWL;         /*  */
-       UCHAR uctCKE;           /*  */
-       UCHAR uctCKRSE;         /*  */
-       UCHAR uctCKRSX;         /*  */
-       UCHAR uctFAW32;         /*  */
-       UCHAR ucReserved1;      /*  */
-       UCHAR ucReserved2;      /*  */
-       UCHAR ucTerminator;
-} ATOM_MEMORY_TIMING_FORMAT_V1;
-
-typedef struct _ATOM_MEMORY_FORMAT {
-       ULONG ulDllDisClock;    /*  memory DLL will be disable when target memory clock is below this clock */
-       union {
-               USHORT usEMRS2Value;    /*  EMRS2 Value is used for GDDR2 and GDDR4 memory type */
-               USHORT usDDR3_Reserved; /*  Not used for DDR3 memory */
-       };
-       union {
-               USHORT usEMRS3Value;    /*  EMRS3 Value is used for GDDR2 and GDDR4 memory type */
-               USHORT usDDR3_MR3;      /*  Used for DDR3 memory */
-       };
-       UCHAR ucMemoryType;     /*  [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now; */
-       UCHAR ucMemoryVenderID; /*  Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed */
-       UCHAR ucRow;            /*  Number of Row,in power of 2; */
-       UCHAR ucColumn;         /*  Number of Column,in power of 2; */
-       UCHAR ucBank;           /*  Nunber of Bank; */
-       UCHAR ucRank;           /*  Number of Rank, in power of 2 */
-       UCHAR ucBurstSize;      /*  burst size, 0= burst size=4  1= burst size=8 */
-       UCHAR ucDllDisBit;      /*  position of DLL Enable/Disable bit in EMRS ( Extended Mode Register ) */
-       UCHAR ucRefreshRateFactor;      /*  memory refresh rate in unit of ms */
-       UCHAR ucDensity;        /*  _8Mx32, _16Mx32, _16Mx16, _32Mx16 */
-       UCHAR ucPreamble;       /* [7:4] Write Preamble, [3:0] Read Preamble */
-       UCHAR ucMemAttrib;      /*  Memory Device Addribute, like RDBI/WDBI etc */
-       ATOM_MEMORY_TIMING_FORMAT asMemTiming[5];       /* Memory Timing block sort from lower clock to higher clock */
-} ATOM_MEMORY_FORMAT;
-
-typedef struct _ATOM_VRAM_MODULE_V3 {
-       ULONG ulChannelMapCfg;  /*  board dependent paramenter:Channel combination */
-       USHORT usSize;          /*  size of ATOM_VRAM_MODULE_V3 */
-       USHORT usDefaultMVDDQ;  /*  board dependent parameter:Default Memory Core Voltage */
-       USHORT usDefaultMVDDC;  /*  board dependent parameter:Default Memory IO Voltage */
-       UCHAR ucExtMemoryID;    /*  An external indicator (by hardcode, callback or pin) to tell what is the current memory module */
-       UCHAR ucChannelNum;     /*  board dependent parameter:Number of channel; */
-       UCHAR ucChannelSize;    /*  board dependent parameter:32bit or 64bit */
-       UCHAR ucVREFI;          /*  board dependnt parameter: EXT or INT +160mv to -140mv */
-       UCHAR ucNPL_RT;         /*  board dependent parameter:NPL round trip delay, used for calculate memory timing parameters */
-       UCHAR ucFlag;           /*  To enable/disable functionalities based on memory type */
-       ATOM_MEMORY_FORMAT asMemory;    /*  describ all of video memory parameters from memory spec */
-} ATOM_VRAM_MODULE_V3;
-
-/* ATOM_VRAM_MODULE_V3.ucNPL_RT */
+typedef struct _ATOM_VRAM_MODULE_V1
+{
+  ULONG                      ulReserved;
+  USHORT                     usEMRSValue;  
+  USHORT                     usMRSValue;
+  USHORT                     usReserved;
+  UCHAR                      ucExtMemoryID;     // An external indicator (by hardcode, callback or pin) to tell what is the current memory module
+  UCHAR                      ucMemoryType;      // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] reserved;
+  UCHAR                      ucMemoryVenderID;  // Predefined,never change across designs or memory type/vender 
+  UCHAR                      ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32...
+  UCHAR                      ucRow;             // Number of Row,in power of 2;
+  UCHAR                      ucColumn;          // Number of Column,in power of 2;
+  UCHAR                      ucBank;            // Nunber of Bank;
+  UCHAR                      ucRank;            // Number of Rank, in power of 2
+  UCHAR                      ucChannelNum;      // Number of channel;
+  UCHAR                      ucChannelConfig;   // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2
+  UCHAR                      ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data;
+  UCHAR                      ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data;
+  UCHAR                      ucReserved[2];
+}ATOM_VRAM_MODULE_V1;
+
+
+typedef struct _ATOM_VRAM_MODULE_V2
+{
+  ULONG                      ulReserved;
+  ULONG                      ulFlags;                          // To enable/disable functionalities based on memory type
+  ULONG                      ulEngineClock;     // Override of default engine clock for particular memory type
+  ULONG                      ulMemoryClock;     // Override of default memory clock for particular memory type
+  USHORT                     usEMRS2Value;      // EMRS2 Value is used for GDDR2 and GDDR4 memory type
+  USHORT                     usEMRS3Value;      // EMRS3 Value is used for GDDR2 and GDDR4 memory type
+  USHORT                     usEMRSValue;  
+  USHORT                     usMRSValue;
+  USHORT                     usReserved;
+  UCHAR                      ucExtMemoryID;     // An external indicator (by hardcode, callback or pin) to tell what is the current memory module
+  UCHAR                      ucMemoryType;      // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now;
+  UCHAR                      ucMemoryVenderID;  // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed
+  UCHAR                      ucMemoryDeviceCfg; // [7:4]=0x0:4M;=0x1:8M;=0x2:16M;0x3:32M....[3:0]=0x0:x4;=0x1:x8;=0x2:x16;=0x3:x32...
+  UCHAR                      ucRow;             // Number of Row,in power of 2;
+  UCHAR                      ucColumn;          // Number of Column,in power of 2;
+  UCHAR                      ucBank;            // Nunber of Bank;
+  UCHAR                      ucRank;            // Number of Rank, in power of 2
+  UCHAR                      ucChannelNum;      // Number of channel;
+  UCHAR                      ucChannelConfig;   // [3:0]=Indication of what channel combination;[4:7]=Channel bit width, in number of 2
+  UCHAR                      ucDefaultMVDDQ_ID; // Default MVDDQ setting for this memory block, ID linking to MVDDQ info table to find real set-up data;
+  UCHAR                      ucDefaultMVDDC_ID; // Default MVDDC setting for this memory block, ID linking to MVDDC info table to find real set-up data;
+  UCHAR                      ucRefreshRateFactor;
+  UCHAR                      ucReserved[3];
+}ATOM_VRAM_MODULE_V2;
+
+
+typedef        struct _ATOM_MEMORY_TIMING_FORMAT
+{
+       ULONG                                                                                    ulClkRange;                            // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing     
+  union{
+         USHORT                                                                                 usMRS;                                                 // mode register                                                
+    USHORT                     usDDR3_MR0;
+  };
+  union{
+         USHORT                                                                                 usEMRS;                                                // extended mode register
+    USHORT                     usDDR3_MR1;
+  };
+       UCHAR                                                                                    ucCL;                                                  // CAS latency
+       UCHAR                                                                                    ucWL;                                                  // WRITE Latency                                
+       UCHAR                                                                                    uctRAS;                                                // tRAS
+       UCHAR                                                                                    uctRC;                                                 // tRC  
+       UCHAR                                                                                    uctRFC;                                                // tRFC
+       UCHAR                                                                                    uctRCDR;                                               // tRCDR        
+       UCHAR                                                                                    uctRCDW;                                               // tRCDW
+       UCHAR                                                                                    uctRP;                                                 // tRP
+       UCHAR                                                                                    uctRRD;                                                // tRRD 
+       UCHAR                                                                                    uctWR;                                                 // tWR
+       UCHAR                                                                                    uctWTR;                                                // tWTR
+       UCHAR                                                                                    uctPDIX;                                               // tPDIX
+       UCHAR                                                                                    uctFAW;                                                // tFAW
+       UCHAR                                                                                    uctAOND;                                               // tAOND
+  union 
+  {
+    struct {
+           UCHAR                                                                                        ucflag;                                                // flag to control memory timing calculation. bit0= control EMRS2 Infineon 
+           UCHAR                                                                                        ucReserved;                                            
+    };
+    USHORT                   usDDR3_MR2;
+  };
+}ATOM_MEMORY_TIMING_FORMAT;
+
+
+typedef        struct _ATOM_MEMORY_TIMING_FORMAT_V1
+{
+       ULONG                                                                                    ulClkRange;                            // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing     
+       USHORT                                                                           usMRS;                                                 // mode register                                                
+       USHORT                                                                           usEMRS;                                                // extended mode register
+       UCHAR                                                                                    ucCL;                                                  // CAS latency
+       UCHAR                                                                                    ucWL;                                                  // WRITE Latency                                
+       UCHAR                                                                                    uctRAS;                                                // tRAS
+       UCHAR                                                                                    uctRC;                                                 // tRC  
+       UCHAR                                                                                    uctRFC;                                                // tRFC
+       UCHAR                                                                                    uctRCDR;                                               // tRCDR        
+       UCHAR                                                                                    uctRCDW;                                               // tRCDW
+       UCHAR                                                                                    uctRP;                                                 // tRP
+       UCHAR                                                                                    uctRRD;                                                // tRRD 
+       UCHAR                                                                                    uctWR;                                                 // tWR
+       UCHAR                                                                                    uctWTR;                                                // tWTR
+       UCHAR                                                                                    uctPDIX;                                               // tPDIX
+       UCHAR                                                                                    uctFAW;                                                // tFAW
+       UCHAR                                                                                    uctAOND;                                               // tAOND
+       UCHAR                                                                                    ucflag;                                                // flag to control memory timing calculation. bit0= control EMRS2 Infineon 
+////////////////////////////////////GDDR parameters///////////////////////////////////
+       UCHAR                                                                                    uctCCDL;                                               // 
+       UCHAR                                                                                    uctCRCRL;                                              // 
+       UCHAR                                                                                    uctCRCWL;                                              // 
+       UCHAR                                                                                    uctCKE;                                                // 
+       UCHAR                                                                                    uctCKRSE;                                              // 
+       UCHAR                                                                                    uctCKRSX;                                              // 
+       UCHAR                                                                                    uctFAW32;                                              // 
+       UCHAR                                                                                    ucMR5lo;                                       // 
+       UCHAR                                                                                    ucMR5hi;                                       // 
+       UCHAR                                                                                    ucTerminator;
+}ATOM_MEMORY_TIMING_FORMAT_V1;
+
+typedef        struct _ATOM_MEMORY_TIMING_FORMAT_V2
+{
+       ULONG                                                                                    ulClkRange;                            // memory clock in 10kHz unit, when target memory clock is below this clock, use this memory timing     
+       USHORT                                                                           usMRS;                                                 // mode register                                                
+       USHORT                                                                           usEMRS;                                                // extended mode register
+       UCHAR                                                                                    ucCL;                                                  // CAS latency
+       UCHAR                                                                                    ucWL;                                                  // WRITE Latency                                
+       UCHAR                                                                                    uctRAS;                                                // tRAS
+       UCHAR                                                                                    uctRC;                                                 // tRC  
+       UCHAR                                                                                    uctRFC;                                                // tRFC
+       UCHAR                                                                                    uctRCDR;                                               // tRCDR        
+       UCHAR                                                                                    uctRCDW;                                               // tRCDW
+       UCHAR                                                                                    uctRP;                                                 // tRP
+       UCHAR                                                                                    uctRRD;                                                // tRRD 
+       UCHAR                                                                                    uctWR;                                                 // tWR
+       UCHAR                                                                                    uctWTR;                                                // tWTR
+       UCHAR                                                                                    uctPDIX;                                               // tPDIX
+       UCHAR                                                                                    uctFAW;                                                // tFAW
+       UCHAR                                                                                    uctAOND;                                               // tAOND
+       UCHAR                                                                                    ucflag;                                                // flag to control memory timing calculation. bit0= control EMRS2 Infineon 
+////////////////////////////////////GDDR parameters///////////////////////////////////
+       UCHAR                                                                                    uctCCDL;                                               // 
+       UCHAR                                                                                    uctCRCRL;                                              // 
+       UCHAR                                                                                    uctCRCWL;                                              // 
+       UCHAR                                                                                    uctCKE;                                                // 
+       UCHAR                                                                                    uctCKRSE;                                              // 
+       UCHAR                                                                                    uctCKRSX;                                              // 
+       UCHAR                                                                                    uctFAW32;                                              // 
+       UCHAR                                                                                    ucMR4lo;                                       // 
+       UCHAR                                                                                    ucMR4hi;                                       // 
+       UCHAR                                                                                    ucMR5lo;                                       // 
+       UCHAR                                                                                    ucMR5hi;                                       // 
+       UCHAR                                                                                    ucTerminator;
+       UCHAR                                                                                    ucReserved;    
+}ATOM_MEMORY_TIMING_FORMAT_V2;
+
+typedef        struct _ATOM_MEMORY_FORMAT
+{
+       ULONG                                                                                    ulDllDisClock;                 // memory DLL will be disable when target memory clock is below this clock
+  union{
+    USHORT                     usEMRS2Value;      // EMRS2 Value is used for GDDR2 and GDDR4 memory type
+    USHORT                     usDDR3_Reserved;   // Not used for DDR3 memory
+  };
+  union{
+    USHORT                     usEMRS3Value;      // EMRS3 Value is used for GDDR2 and GDDR4 memory type
+    USHORT                     usDDR3_MR3;        // Used for DDR3 memory
+  };
+  UCHAR                      ucMemoryType;      // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4;[3:0] - must not be used for now;
+  UCHAR                      ucMemoryVenderID;  // Predefined,never change across designs or memory type/vender. If not predefined, vendor detection table gets executed
+  UCHAR                      ucRow;             // Number of Row,in power of 2;
+  UCHAR                      ucColumn;          // Number of Column,in power of 2;
+  UCHAR                      ucBank;            // Nunber of Bank;
+  UCHAR                      ucRank;            // Number of Rank, in power of 2
+       UCHAR                                                                                    ucBurstSize;                           // burst size, 0= burst size=4  1= burst size=8
+  UCHAR                      ucDllDisBit;                              // position of DLL Enable/Disable bit in EMRS ( Extended Mode Register )
+  UCHAR                      ucRefreshRateFactor;      // memory refresh rate in unit of ms    
+       UCHAR                                                                                    ucDensity;                                     // _8Mx32, _16Mx32, _16Mx16, _32Mx16
+       UCHAR                                                                                    ucPreamble;                            //[7:4] Write Preamble, [3:0] Read Preamble
+  UCHAR                                                                                         ucMemAttrib;                           // Memory Device Addribute, like RDBI/WDBI etc
+       ATOM_MEMORY_TIMING_FORMAT        asMemTiming[5];                //Memory Timing block sort from lower clock to higher clock
+}ATOM_MEMORY_FORMAT;
+
+
+typedef struct _ATOM_VRAM_MODULE_V3
+{
+       ULONG                                                                                    ulChannelMapCfg;               // board dependent paramenter:Channel combination
+       USHORT                                                                           usSize;                                                // size of ATOM_VRAM_MODULE_V3
+  USHORT                     usDefaultMVDDQ;           // board dependent parameter:Default Memory Core Voltage
+  USHORT                     usDefaultMVDDC;           // board dependent parameter:Default Memory IO Voltage
+       UCHAR                      ucExtMemoryID;     // An external indicator (by hardcode, callback or pin) to tell what is the current memory module
+  UCHAR                      ucChannelNum;      // board dependent parameter:Number of channel;
+       UCHAR                                                                                    ucChannelSize;                 // board dependent parameter:32bit or 64bit     
+       UCHAR                                                                                    ucVREFI;                                               // board dependnt parameter: EXT or INT +160mv to -140mv
+       UCHAR                                                                                    ucNPL_RT;                                      // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters
+       UCHAR                                                                                    ucFlag;                                                // To enable/disable functionalities based on memory type
+       ATOM_MEMORY_FORMAT                               asMemory;                                      // describ all of video memory parameters from memory spec
+}ATOM_VRAM_MODULE_V3;
+
+
+//ATOM_VRAM_MODULE_V3.ucNPL_RT
 #define NPL_RT_MASK                                                                                                                    0x0f
 #define BATTERY_ODT_MASK                                                                                               0xc0
 
 #define ATOM_VRAM_MODULE                ATOM_VRAM_MODULE_V3
 
-typedef struct _ATOM_VRAM_MODULE_V4 {
-       ULONG ulChannelMapCfg;  /*  board dependent parameter: Channel combination */
-       USHORT usModuleSize;    /*  size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE */
-       USHORT usPrivateReserved;       /*  BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! */
-       /*  MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) */
-       USHORT usReserved;
-       UCHAR ucExtMemoryID;    /*  An external indicator (by hardcode, callback or pin) to tell what is the current memory module */
-       UCHAR ucMemoryType;     /*  [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; */
-       UCHAR ucChannelNum;     /*  Number of channels present in this module config */
-       UCHAR ucChannelWidth;   /*  0 - 32 bits; 1 - 64 bits */
-       UCHAR ucDensity;        /*  _8Mx32, _16Mx32, _16Mx16, _32Mx16 */
-       UCHAR ucFlag;           /*  To enable/disable functionalities based on memory type */
-       UCHAR ucMisc;           /*  bit0: 0 - single rank; 1 - dual rank;   bit2: 0 - burstlength 4, 1 - burstlength 8 */
-       UCHAR ucVREFI;          /*  board dependent parameter */
-       UCHAR ucNPL_RT;         /*  board dependent parameter:NPL round trip delay, used for calculate memory timing parameters */
-       UCHAR ucPreamble;       /*  [7:4] Write Preamble, [3:0] Read Preamble */
-       UCHAR ucMemorySize;     /*  BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! */
-       /*  Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros */
-       UCHAR ucReserved[3];
-
-/* compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level */
-       union {
-               USHORT usEMRS2Value;    /*  EMRS2 Value is used for GDDR2 and GDDR4 memory type */
-               USHORT usDDR3_Reserved;
-       };
-       union {
-               USHORT usEMRS3Value;    /*  EMRS3 Value is used for GDDR2 and GDDR4 memory type */
-               USHORT usDDR3_MR3;      /*  Used for DDR3 memory */
-       };
-       UCHAR ucMemoryVenderID; /*  Predefined, If not predefined, vendor detection table gets executed */
-       UCHAR ucRefreshRateFactor;      /*  [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) */
-       UCHAR ucReserved2[2];
-       ATOM_MEMORY_TIMING_FORMAT asMemTiming[5];       /* Memory Timing block sort from lower clock to higher clock */
-} ATOM_VRAM_MODULE_V4;
+typedef struct _ATOM_VRAM_MODULE_V4
+{
+  ULONG          ulChannelMapCfg;                      // board dependent parameter: Channel combination
+  USHORT  usModuleSize;                     // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE
+  USHORT  usPrivateReserved;                // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!!
+                                            // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS)
+  USHORT  usReserved;
+  UCHAR   ucExtMemoryID;                           // An external indicator (by hardcode, callback or pin) to tell what is the current memory module
+  UCHAR   ucMemoryType;                     // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now;
+  UCHAR   ucChannelNum;                     // Number of channels present in this module config
+  UCHAR   ucChannelWidth;                   // 0 - 32 bits; 1 - 64 bits
+       UCHAR   ucDensity;                        // _8Mx32, _16Mx32, _16Mx16, _32Mx16
+       UCHAR     ucFlag;                                                               // To enable/disable functionalities based on memory type
+       UCHAR     ucMisc;                                                               // bit0: 0 - single rank; 1 - dual rank;   bit2: 0 - burstlength 4, 1 - burstlength 8
+  UCHAR                ucVREFI;                          // board dependent parameter
+  UCHAR   ucNPL_RT;                         // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters
+  UCHAR                ucPreamble;                       // [7:4] Write Preamble, [3:0] Read Preamble
+  UCHAR   ucMemorySize;                     // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!!
+                                            // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros
+  UCHAR   ucReserved[3];
+
+//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level
+  union{
+    USHORT     usEMRS2Value;                   // EMRS2 Value is used for GDDR2 and GDDR4 memory type
+    USHORT  usDDR3_Reserved;
+  };
+  union{
+    USHORT     usEMRS3Value;                   // EMRS3 Value is used for GDDR2 and GDDR4 memory type
+    USHORT  usDDR3_MR3;                     // Used for DDR3 memory
+  };  
+  UCHAR   ucMemoryVenderID;                        // Predefined, If not predefined, vendor detection table gets executed
+  UCHAR          ucRefreshRateFactor;              // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms)
+  UCHAR   ucReserved2[2];
+  ATOM_MEMORY_TIMING_FORMAT  asMemTiming[5];//Memory Timing block sort from lower clock to higher clock
+}ATOM_VRAM_MODULE_V4;
 
 #define VRAM_MODULE_V4_MISC_RANK_MASK       0x3
 #define VRAM_MODULE_V4_MISC_DUAL_RANK       0x1
@@ -3973,96 +4841,139 @@ typedef struct _ATOM_VRAM_MODULE_V4 {
 #define VRAM_MODULE_V4_MISC_BL8             0x4
 #define VRAM_MODULE_V4_MISC_DUAL_CS         0x10
 
-typedef struct _ATOM_VRAM_MODULE_V5 {
-       ULONG ulChannelMapCfg;  /*  board dependent parameter: Channel combination */
-       USHORT usModuleSize;    /*  size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE */
-       USHORT usPrivateReserved;       /*  BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! */
-       /*  MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS) */
-       USHORT usReserved;
-       UCHAR ucExtMemoryID;    /*  An external indicator (by hardcode, callback or pin) to tell what is the current memory module */
-       UCHAR ucMemoryType;     /*  [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now; */
-       UCHAR ucChannelNum;     /*  Number of channels present in this module config */
-       UCHAR ucChannelWidth;   /*  0 - 32 bits; 1 - 64 bits */
-       UCHAR ucDensity;        /*  _8Mx32, _16Mx32, _16Mx16, _32Mx16 */
-       UCHAR ucFlag;           /*  To enable/disable functionalities based on memory type */
-       UCHAR ucMisc;           /*  bit0: 0 - single rank; 1 - dual rank;   bit2: 0 - burstlength 4, 1 - burstlength 8 */
-       UCHAR ucVREFI;          /*  board dependent parameter */
-       UCHAR ucNPL_RT;         /*  board dependent parameter:NPL round trip delay, used for calculate memory timing parameters */
-       UCHAR ucPreamble;       /*  [7:4] Write Preamble, [3:0] Read Preamble */
-       UCHAR ucMemorySize;     /*  BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!! */
-       /*  Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros */
-       UCHAR ucReserved[3];
+typedef struct _ATOM_VRAM_MODULE_V5
+{
+  ULONG          ulChannelMapCfg;                      // board dependent parameter: Channel combination
+  USHORT  usModuleSize;                     // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE
+  USHORT  usPrivateReserved;                // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!!
+                                            // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS)
+  USHORT  usReserved;
+  UCHAR   ucExtMemoryID;                           // An external indicator (by hardcode, callback or pin) to tell what is the current memory module
+  UCHAR   ucMemoryType;                     // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now;
+  UCHAR   ucChannelNum;                     // Number of channels present in this module config
+  UCHAR   ucChannelWidth;                   // 0 - 32 bits; 1 - 64 bits
+       UCHAR   ucDensity;                        // _8Mx32, _16Mx32, _16Mx16, _32Mx16
+       UCHAR     ucFlag;                                                               // To enable/disable functionalities based on memory type
+       UCHAR     ucMisc;                                                               // bit0: 0 - single rank; 1 - dual rank;   bit2: 0 - burstlength 4, 1 - burstlength 8
+  UCHAR                ucVREFI;                          // board dependent parameter
+  UCHAR   ucNPL_RT;                         // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters
+  UCHAR                ucPreamble;                       // [7:4] Write Preamble, [3:0] Read Preamble
+  UCHAR   ucMemorySize;                     // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!!
+                                            // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros
+  UCHAR   ucReserved[3];
+
+//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level
+  USHORT       usEMRS2Value;                               // EMRS2 Value is used for GDDR2 and GDDR4 memory type
+  USHORT       usEMRS3Value;                               // EMRS3 Value is used for GDDR2 and GDDR4 memory type
+  UCHAR   ucMemoryVenderID;                        // Predefined, If not predefined, vendor detection table gets executed
+  UCHAR          ucRefreshRateFactor;              // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms)
+  UCHAR          ucFIFODepth;                                  // FIFO depth supposes to be detected during vendor detection, but if we dont do vendor detection we have to hardcode FIFO Depth
+  UCHAR   ucCDR_Bandwidth;                // [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth
+  ATOM_MEMORY_TIMING_FORMAT_V1  asMemTiming[5];//Memory Timing block sort from lower clock to higher clock
+}ATOM_VRAM_MODULE_V5;
+
+typedef struct _ATOM_VRAM_MODULE_V6
+{
+  ULONG          ulChannelMapCfg;                      // board dependent parameter: Channel combination
+  USHORT  usModuleSize;                     // size of ATOM_VRAM_MODULE_V4, make it easy for VBIOS to look for next entry of VRAM_MODULE
+  USHORT  usPrivateReserved;                // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!!
+                                            // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS)
+  USHORT  usReserved;
+  UCHAR   ucExtMemoryID;                           // An external indicator (by hardcode, callback or pin) to tell what is the current memory module
+  UCHAR   ucMemoryType;                     // [7:4]=0x1:DDR1;=0x2:DDR2;=0x3:DDR3;=0x4:DDR4; 0x5:DDR5 [3:0] - Must be 0x0 for now;
+  UCHAR   ucChannelNum;                     // Number of channels present in this module config
+  UCHAR   ucChannelWidth;                   // 0 - 32 bits; 1 - 64 bits
+       UCHAR   ucDensity;                        // _8Mx32, _16Mx32, _16Mx16, _32Mx16
+       UCHAR     ucFlag;                                                               // To enable/disable functionalities based on memory type
+       UCHAR     ucMisc;                                                               // bit0: 0 - single rank; 1 - dual rank;   bit2: 0 - burstlength 4, 1 - burstlength 8
+  UCHAR                ucVREFI;                          // board dependent parameter
+  UCHAR   ucNPL_RT;                         // board dependent parameter:NPL round trip delay, used for calculate memory timing parameters
+  UCHAR                ucPreamble;                       // [7:4] Write Preamble, [3:0] Read Preamble
+  UCHAR   ucMemorySize;                     // BIOS internal reserved space to optimize code size, updated by the compiler, shouldn't be modified manually!!
+                                            // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros
+  UCHAR   ucReserved[3];
+
+//compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level
+  USHORT       usEMRS2Value;                               // EMRS2 Value is used for GDDR2 and GDDR4 memory type
+  USHORT       usEMRS3Value;                               // EMRS3 Value is used for GDDR2 and GDDR4 memory type
+  UCHAR   ucMemoryVenderID;                        // Predefined, If not predefined, vendor detection table gets executed
+  UCHAR          ucRefreshRateFactor;              // [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms)
+  UCHAR          ucFIFODepth;                                  // FIFO depth supposes to be detected during vendor detection, but if we dont do vendor detection we have to hardcode FIFO Depth
+  UCHAR   ucCDR_Bandwidth;                // [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth
+  ATOM_MEMORY_TIMING_FORMAT_V2  asMemTiming[5];//Memory Timing block sort from lower clock to higher clock
+}ATOM_VRAM_MODULE_V6;
+
+
+
+typedef struct _ATOM_VRAM_INFO_V2
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+  UCHAR                      ucNumOfVRAMModule;
+  ATOM_VRAM_MODULE           aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];      // just for allocation, real number of blocks is in ucNumOfVRAMModule;
+}ATOM_VRAM_INFO_V2;
 
-/* compare with V3, we flat the struct by merging ATOM_MEMORY_FORMAT (as is) into V4 as the same level */
-       USHORT usEMRS2Value;    /*  EMRS2 Value is used for GDDR2 and GDDR4 memory type */
-       USHORT usEMRS3Value;    /*  EMRS3 Value is used for GDDR2 and GDDR4 memory type */
-       UCHAR ucMemoryVenderID; /*  Predefined, If not predefined, vendor detection table gets executed */
-       UCHAR ucRefreshRateFactor;      /*  [1:0]=RefreshFactor (00=8ms, 01=16ms, 10=32ms,11=64ms) */
-       UCHAR ucFIFODepth;      /*  FIFO depth supposes to be detected during vendor detection, but if we dont do vendor detection we have to hardcode FIFO Depth */
-       UCHAR ucCDR_Bandwidth;  /*  [0:3]=Read CDR bandwidth, [4:7] - Write CDR Bandwidth */
-       ATOM_MEMORY_TIMING_FORMAT_V1 asMemTiming[5];    /* Memory Timing block sort from lower clock to higher clock */
-} ATOM_VRAM_MODULE_V5;
-
-typedef struct _ATOM_VRAM_INFO_V2 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucNumOfVRAMModule;
-       ATOM_VRAM_MODULE aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];     /*  just for allocation, real number of blocks is in ucNumOfVRAMModule; */
-} ATOM_VRAM_INFO_V2;
-
-typedef struct _ATOM_VRAM_INFO_V3 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usMemAdjustTblOffset;    /*  offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting */
-       USHORT usMemClkPatchTblOffset;  /*      offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting */
-       USHORT usRerseved;
-       UCHAR aVID_PinsShift[9];        /*  8 bit strap maximum+terminator */
-       UCHAR ucNumOfVRAMModule;
-       ATOM_VRAM_MODULE aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];     /*  just for allocation, real number of blocks is in ucNumOfVRAMModule; */
-       ATOM_INIT_REG_BLOCK asMemPatch; /*  for allocation */
-       /*      ATOM_INIT_REG_BLOCK                              aMemAdjust; */
-} ATOM_VRAM_INFO_V3;
+typedef struct _ATOM_VRAM_INFO_V3
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+       USHORT                                                                           usMemAdjustTblOffset;                                                                                                   // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting
+       USHORT                                                                           usMemClkPatchTblOffset;                                                                                                 //     offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting
+       USHORT                                                                           usRerseved;
+       UCHAR                            aVID_PinsShift[9];                                                                                                                      // 8 bit strap maximum+terminator
+  UCHAR                      ucNumOfVRAMModule;
+  ATOM_VRAM_MODULE                    aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];      // just for allocation, real number of blocks is in ucNumOfVRAMModule;
+       ATOM_INIT_REG_BLOCK                              asMemPatch;                                                                                                                                             // for allocation
+                                                                                                                                                                                                                                                                                                                //     ATOM_INIT_REG_BLOCK                              aMemAdjust;
+}ATOM_VRAM_INFO_V3;
 
 #define        ATOM_VRAM_INFO_LAST          ATOM_VRAM_INFO_V3
 
-typedef struct _ATOM_VRAM_INFO_V4 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usMemAdjustTblOffset;    /*  offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting */
-       USHORT usMemClkPatchTblOffset;  /*      offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting */
-       USHORT usRerseved;
-       UCHAR ucMemDQ7_0ByteRemap;      /*  DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3 */
-       ULONG ulMemDQ7_0BitRemap;       /*  each DQ line ( 7~0) use 3bits, like: DQ0=Bit[2:0], DQ1:[5:3], ... DQ7:[23:21] */
-       UCHAR ucReservde[4];
-       UCHAR ucNumOfVRAMModule;
-       ATOM_VRAM_MODULE_V4 aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];  /*  just for allocation, real number of blocks is in ucNumOfVRAMModule; */
-       ATOM_INIT_REG_BLOCK asMemPatch; /*  for allocation */
-       /*      ATOM_INIT_REG_BLOCK                              aMemAdjust; */
-} ATOM_VRAM_INFO_V4;
-
-typedef struct _ATOM_VRAM_GPIO_DETECTION_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR aVID_PinsShift[9];        /* 8 bit strap maximum+terminator */
-} ATOM_VRAM_GPIO_DETECTION_INFO;
-
-typedef struct _ATOM_MEMORY_TRAINING_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucTrainingLoop;
-       UCHAR ucReserved[3];
-       ATOM_INIT_REG_BLOCK asMemTrainingSetting;
-} ATOM_MEMORY_TRAINING_INFO;
-
-typedef struct SW_I2C_CNTL_DATA_PARAMETERS {
-       UCHAR ucControl;
-       UCHAR ucData;
-       UCHAR ucSatus;
-       UCHAR ucTemp;
+typedef struct _ATOM_VRAM_INFO_V4
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+       USHORT                                                                           usMemAdjustTblOffset;                                                                                                   // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting
+       USHORT                                                                           usMemClkPatchTblOffset;                                                                                                 //     offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting
+       USHORT                                                                           usRerseved;
+       UCHAR                            ucMemDQ7_0ByteRemap;                                                                                                      // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3
+  ULONG                      ulMemDQ7_0BitRemap;                             // each DQ line ( 7~0) use 3bits, like: DQ0=Bit[2:0], DQ1:[5:3], ... DQ7:[23:21]
+  UCHAR                      ucReservde[4]; 
+  UCHAR                      ucNumOfVRAMModule;
+  ATOM_VRAM_MODULE_V4               aVramInfo[ATOM_MAX_NUMBER_OF_VRAM_MODULE];      // just for allocation, real number of blocks is in ucNumOfVRAMModule;
+       ATOM_INIT_REG_BLOCK                              asMemPatch;                                                                                                                                             // for allocation
+                                                                                                                                                                                                                                                                                                                //     ATOM_INIT_REG_BLOCK                              aMemAdjust;
+}ATOM_VRAM_INFO_V4;
+
+typedef struct _ATOM_VRAM_GPIO_DETECTION_INFO
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;
+  UCHAR                         aVID_PinsShift[9];   //8 bit strap maximum+terminator
+}ATOM_VRAM_GPIO_DETECTION_INFO;
+
+
+typedef struct _ATOM_MEMORY_TRAINING_INFO
+{
+       ATOM_COMMON_TABLE_HEADER   sHeader;
+       UCHAR                                                                                    ucTrainingLoop;
+       UCHAR                                                                                    ucReserved[3];
+       ATOM_INIT_REG_BLOCK                              asMemTrainingSetting;
+}ATOM_MEMORY_TRAINING_INFO;
+
+
+typedef struct SW_I2C_CNTL_DATA_PARAMETERS
+{
+  UCHAR    ucControl;
+  UCHAR    ucData; 
+  UCHAR    ucSatus; 
+  UCHAR    ucTemp; 
 } SW_I2C_CNTL_DATA_PARAMETERS;
 
 #define SW_I2C_CNTL_DATA_PS_ALLOCATION  SW_I2C_CNTL_DATA_PARAMETERS
 
-typedef struct _SW_I2C_IO_DATA_PARAMETERS {
-       USHORT GPIO_Info;
-       UCHAR ucAct;
-       UCHAR ucData;
-} SW_I2C_IO_DATA_PARAMETERS;
+typedef struct _SW_I2C_IO_DATA_PARAMETERS
+{                               
+  USHORT   GPIO_Info;
+  UCHAR    ucAct; 
+  UCHAR    ucData; 
+ } SW_I2C_IO_DATA_PARAMETERS;
 
 #define SW_I2C_IO_DATA_PS_ALLOCATION  SW_I2C_IO_DATA_PARAMETERS
 
@@ -4087,127 +4998,136 @@ typedef struct _SW_I2C_IO_DATA_PARAMETERS {
 #define SW_I2C_CNTL_CLOSE     5
 #define SW_I2C_CNTL_WRITE1BIT 6
 
-/* ==============================VESA definition Portion=============================== */
+//==============================VESA definition Portion===============================
 #define VESA_OEM_PRODUCT_REV                               '01.00'
-#define VESA_MODE_ATTRIBUTE_MODE_SUPPORT            0xBB       /* refer to VBE spec p.32, no TTY support */
+#define VESA_MODE_ATTRIBUTE_MODE_SUPPORT            0xBB       //refer to VBE spec p.32, no TTY support
 #define VESA_MODE_WIN_ATTRIBUTE                                                     7
 #define VESA_WIN_SIZE                                                                                       64
 
-typedef struct _PTR_32_BIT_STRUCTURE {
-       USHORT Offset16;
-       USHORT Segment16;
+typedef struct _PTR_32_BIT_STRUCTURE
+{
+       USHORT  Offset16;                       
+       USHORT  Segment16;                              
 } PTR_32_BIT_STRUCTURE;
 
-typedef union _PTR_32_BIT_UNION {
-       PTR_32_BIT_STRUCTURE SegmentOffset;
-       ULONG Ptr32_Bit;
+typedef union _PTR_32_BIT_UNION
+{
+       PTR_32_BIT_STRUCTURE    SegmentOffset;
+       ULONG                                           Ptr32_Bit;
 } PTR_32_BIT_UNION;
 
-typedef struct _VBE_1_2_INFO_BLOCK_UPDATABLE {
-       UCHAR VbeSignature[4];
-       USHORT VbeVersion;
-       PTR_32_BIT_UNION OemStringPtr;
-       UCHAR Capabilities[4];
-       PTR_32_BIT_UNION VideoModePtr;
-       USHORT TotalMemory;
+typedef struct _VBE_1_2_INFO_BLOCK_UPDATABLE
+{
+       UCHAR                                 VbeSignature[4];
+       USHORT                              VbeVersion;
+       PTR_32_BIT_UNION        OemStringPtr;
+       UCHAR                                 Capabilities[4];
+       PTR_32_BIT_UNION        VideoModePtr;
+       USHORT                              TotalMemory;
 } VBE_1_2_INFO_BLOCK_UPDATABLE;
 
-typedef struct _VBE_2_0_INFO_BLOCK_UPDATABLE {
-       VBE_1_2_INFO_BLOCK_UPDATABLE CommonBlock;
-       USHORT OemSoftRev;
-       PTR_32_BIT_UNION OemVendorNamePtr;
-       PTR_32_BIT_UNION OemProductNamePtr;
-       PTR_32_BIT_UNION OemProductRevPtr;
+
+typedef struct _VBE_2_0_INFO_BLOCK_UPDATABLE
+{
+       VBE_1_2_INFO_BLOCK_UPDATABLE    CommonBlock;
+       USHORT                                                      OemSoftRev;
+       PTR_32_BIT_UNION                                OemVendorNamePtr;
+       PTR_32_BIT_UNION                                OemProductNamePtr;
+       PTR_32_BIT_UNION                                OemProductRevPtr;
 } VBE_2_0_INFO_BLOCK_UPDATABLE;
 
-typedef union _VBE_VERSION_UNION {
-       VBE_2_0_INFO_BLOCK_UPDATABLE VBE_2_0_InfoBlock;
-       VBE_1_2_INFO_BLOCK_UPDATABLE VBE_1_2_InfoBlock;
+typedef union _VBE_VERSION_UNION
+{
+       VBE_2_0_INFO_BLOCK_UPDATABLE    VBE_2_0_InfoBlock;
+       VBE_1_2_INFO_BLOCK_UPDATABLE    VBE_1_2_InfoBlock;
 } VBE_VERSION_UNION;
 
-typedef struct _VBE_INFO_BLOCK {
-       VBE_VERSION_UNION UpdatableVBE_Info;
-       UCHAR Reserved[222];
-       UCHAR OemData[256];
+typedef struct _VBE_INFO_BLOCK
+{
+       VBE_VERSION_UNION                       UpdatableVBE_Info;
+       UCHAR                                                 Reserved[222];
+       UCHAR                                                 OemData[256];
 } VBE_INFO_BLOCK;
 
-typedef struct _VBE_FP_INFO {
-       USHORT HSize;
-       USHORT VSize;
-       USHORT FPType;
-       UCHAR RedBPP;
-       UCHAR GreenBPP;
-       UCHAR BlueBPP;
-       UCHAR ReservedBPP;
-       ULONG RsvdOffScrnMemSize;
-       ULONG RsvdOffScrnMEmPtr;
-       UCHAR Reserved[14];
+typedef struct _VBE_FP_INFO
+{
+  USHORT       HSize;
+       USHORT  VSize;
+       USHORT  FPType;
+       UCHAR           RedBPP;
+       UCHAR           GreenBPP;
+       UCHAR           BlueBPP;
+       UCHAR           ReservedBPP;
+       ULONG           RsvdOffScrnMemSize;
+       ULONG           RsvdOffScrnMEmPtr;
+       UCHAR           Reserved[14];
 } VBE_FP_INFO;
 
-typedef struct _VESA_MODE_INFO_BLOCK {
-/*  Mandatory information for all VBE revisions */
-       USHORT ModeAttributes;  /*                  dw      ?       ; mode attributes */
-       UCHAR WinAAttributes;   /*                    db      ?       ; window A attributes */
-       UCHAR WinBAttributes;   /*                    db      ?       ; window B attributes */
-       USHORT WinGranularity;  /*                    dw      ?       ; window granularity */
-       USHORT WinSize;         /*                    dw      ?       ; window size */
-       USHORT WinASegment;     /*                    dw      ?       ; window A start segment */
-       USHORT WinBSegment;     /*                    dw      ?       ; window B start segment */
-       ULONG WinFuncPtr;       /*                    dd      ?       ; real mode pointer to window function */
-       USHORT BytesPerScanLine;        /*                    dw      ?       ; bytes per scan line */
-
-/* ; Mandatory information for VBE 1.2 and above */
-       USHORT XResolution;     /*                         dw      ?       ; horizontal resolution in pixels or characters */
-       USHORT YResolution;     /*                   dw      ?       ; vertical resolution in pixels or characters */
-       UCHAR XCharSize;        /*                   db      ?       ; character cell width in pixels */
-       UCHAR YCharSize;        /*                   db      ?       ; character cell height in pixels */
-       UCHAR NumberOfPlanes;   /*                   db      ?       ; number of memory planes */
-       UCHAR BitsPerPixel;     /*                   db      ?       ; bits per pixel */
-       UCHAR NumberOfBanks;    /*                   db      ?       ; number of banks */
-       UCHAR MemoryModel;      /*                   db      ?       ; memory model type */
-       UCHAR BankSize;         /*                   db      ?       ; bank size in KB */
-       UCHAR NumberOfImagePages;       /*            db    ?       ; number of images */
-       UCHAR ReservedForPageFunction;  /* db  1       ; reserved for page function */
-
-/* ; Direct Color fields(required for direct/6 and YUV/7 memory models) */
-       UCHAR RedMaskSize;      /*           db      ?       ; size of direct color red mask in bits */
-       UCHAR RedFieldPosition; /*           db      ?       ; bit position of lsb of red mask */
-       UCHAR GreenMaskSize;    /*           db      ?       ; size of direct color green mask in bits */
-       UCHAR GreenFieldPosition;       /*           db      ?       ; bit position of lsb of green mask */
-       UCHAR BlueMaskSize;     /*           db      ?       ; size of direct color blue mask in bits */
-       UCHAR BlueFieldPosition;        /*           db      ?       ; bit position of lsb of blue mask */
-       UCHAR RsvdMaskSize;     /*           db      ?       ; size of direct color reserved mask in bits */
-       UCHAR RsvdFieldPosition;        /*           db      ?       ; bit position of lsb of reserved mask */
-       UCHAR DirectColorModeInfo;      /*           db      ?       ; direct color mode attributes */
-
-/* ; Mandatory information for VBE 2.0 and above */
-       ULONG PhysBasePtr;      /*           dd      ?       ; physical address for flat memory frame buffer */
-       ULONG Reserved_1;       /*           dd      0       ; reserved - always set to 0 */
-       USHORT Reserved_2;      /*     dw    0       ; reserved - always set to 0 */
-
-/* ; Mandatory information for VBE 3.0 and above */
-       USHORT LinBytesPerScanLine;     /*         dw      ?       ; bytes per scan line for linear modes */
-       UCHAR BnkNumberOfImagePages;    /*         db      ?       ; number of images for banked modes */
-       UCHAR LinNumberOfImagPages;     /*         db      ?       ; number of images for linear modes */
-       UCHAR LinRedMaskSize;   /*         db      ?       ; size of direct color red mask(linear modes) */
-       UCHAR LinRedFieldPosition;      /*         db      ?       ; bit position of lsb of red mask(linear modes) */
-       UCHAR LinGreenMaskSize; /*         db      ?       ; size of direct color green mask(linear modes) */
-       UCHAR LinGreenFieldPosition;    /*         db      ?       ; bit position of lsb of green mask(linear modes) */
-       UCHAR LinBlueMaskSize;  /*         db      ?       ; size of direct color blue mask(linear modes) */
-       UCHAR LinBlueFieldPosition;     /*         db      ?       ; bit position of lsb of blue mask(linear modes) */
-       UCHAR LinRsvdMaskSize;  /*         db      ?       ; size of direct color reserved mask(linear modes) */
-       UCHAR LinRsvdFieldPosition;     /*         db      ?       ; bit position of lsb of reserved mask(linear modes) */
-       ULONG MaxPixelClock;    /*         dd      ?       ; maximum pixel clock(in Hz) for graphics mode */
-       UCHAR Reserved;         /*         db      190 dup (0) */
+typedef struct _VESA_MODE_INFO_BLOCK
+{
+// Mandatory information for all VBE revisions
+  USHORT    ModeAttributes;  //                        dw      ?       ; mode attributes
+       UCHAR     WinAAttributes;  //                   db      ?       ; window A attributes
+       UCHAR     WinBAttributes;  //                   db      ?       ; window B attributes
+       USHORT    WinGranularity;  //                   dw      ?       ; window granularity
+       USHORT    WinSize;         //                   dw      ?       ; window size
+       USHORT    WinASegment;     //                   dw      ?       ; window A start segment
+       USHORT    WinBSegment;     //                   dw      ?       ; window B start segment
+       ULONG     WinFuncPtr;      //                   dd      ?       ; real mode pointer to window function
+       USHORT    BytesPerScanLine;//                   dw      ?       ; bytes per scan line
+
+//; Mandatory information for VBE 1.2 and above
+  USHORT    XResolution;      //                       dw      ?       ; horizontal resolution in pixels or characters
+       USHORT    YResolution;      //                  dw      ?       ; vertical resolution in pixels or characters
+       UCHAR     XCharSize;        //                  db      ?       ; character cell width in pixels
+       UCHAR     YCharSize;        //                  db      ?       ; character cell height in pixels
+       UCHAR     NumberOfPlanes;   //                  db      ?       ; number of memory planes
+       UCHAR     BitsPerPixel;     //                  db      ?       ; bits per pixel
+       UCHAR     NumberOfBanks;    //                  db      ?       ; number of banks
+       UCHAR     MemoryModel;      //                  db      ?       ; memory model type
+       UCHAR     BankSize;         //                  db      ?       ; bank size in KB
+       UCHAR     NumberOfImagePages;//           db    ?       ; number of images
+       UCHAR     ReservedForPageFunction;//db  1       ; reserved for page function
+
+//; Direct Color fields(required for direct/6 and YUV/7 memory models)
+       UCHAR                   RedMaskSize;        //          db      ?       ; size of direct color red mask in bits
+       UCHAR                   RedFieldPosition;   //          db      ?       ; bit position of lsb of red mask
+       UCHAR                   GreenMaskSize;      //          db      ?       ; size of direct color green mask in bits
+       UCHAR                   GreenFieldPosition; //          db      ?       ; bit position of lsb of green mask
+       UCHAR                   BlueMaskSize;       //          db      ?       ; size of direct color blue mask in bits
+       UCHAR                   BlueFieldPosition;  //          db      ?       ; bit position of lsb of blue mask
+       UCHAR                   RsvdMaskSize;       //          db      ?       ; size of direct color reserved mask in bits
+       UCHAR                   RsvdFieldPosition;  //          db      ?       ; bit position of lsb of reserved mask
+       UCHAR                   DirectColorModeInfo;//          db      ?       ; direct color mode attributes
+
+//; Mandatory information for VBE 2.0 and above
+       ULONG                   PhysBasePtr;        //          dd      ?       ; physical address for flat memory frame buffer
+       ULONG                   Reserved_1;         //          dd      0       ; reserved - always set to 0
+       USHORT          Reserved_2;         //    dw    0       ; reserved - always set to 0
+
+//; Mandatory information for VBE 3.0 and above
+       USHORT          LinBytesPerScanLine;  //        dw      ?       ; bytes per scan line for linear modes
+       UCHAR                   BnkNumberOfImagePages;//        db      ?       ; number of images for banked modes
+       UCHAR                   LinNumberOfImagPages; //        db      ?       ; number of images for linear modes
+       UCHAR                   LinRedMaskSize;       //        db      ?       ; size of direct color red mask(linear modes)
+       UCHAR                   LinRedFieldPosition;  //        db      ?       ; bit position of lsb of red mask(linear modes)
+       UCHAR                   LinGreenMaskSize;     //        db      ?       ; size of direct color green mask(linear modes)
+       UCHAR                   LinGreenFieldPosition;//        db      ?       ; bit position of lsb of green mask(linear modes)
+       UCHAR                   LinBlueMaskSize;      //        db      ?       ; size of direct color blue mask(linear modes)
+       UCHAR                   LinBlueFieldPosition; //        db      ?       ; bit position of lsb of blue mask(linear modes)
+       UCHAR                   LinRsvdMaskSize;      //        db      ?       ; size of direct color reserved mask(linear modes)
+       UCHAR                   LinRsvdFieldPosition; //        db      ?       ; bit position of lsb of reserved mask(linear modes)
+       ULONG                   MaxPixelClock;        //        dd      ?       ; maximum pixel clock(in Hz) for graphics mode
+       UCHAR                   Reserved;             //        db      190 dup (0)
 } VESA_MODE_INFO_BLOCK;
 
-/*  BIOS function CALLS */
-#define ATOM_BIOS_EXTENDED_FUNCTION_CODE        0xA0   /*  ATI Extended Function code */
+// BIOS function CALLS
+#define ATOM_BIOS_EXTENDED_FUNCTION_CODE        0xA0           // ATI Extended Function code
 #define ATOM_BIOS_FUNCTION_COP_MODE             0x00
 #define ATOM_BIOS_FUNCTION_SHORT_QUERY1         0x04
 #define ATOM_BIOS_FUNCTION_SHORT_QUERY2         0x05
 #define ATOM_BIOS_FUNCTION_SHORT_QUERY3         0x06
-#define ATOM_BIOS_FUNCTION_GET_DDC              0x0B
+#define ATOM_BIOS_FUNCTION_GET_DDC              0x0B   
 #define ATOM_BIOS_FUNCTION_ASIC_DSTATE          0x0E
 #define ATOM_BIOS_FUNCTION_DEBUG_PLAY           0x0F
 #define ATOM_BIOS_FUNCTION_STV_STD              0x16
@@ -4217,100 +5137,135 @@ typedef struct _VESA_MODE_INFO_BLOCK {
 #define ATOM_BIOS_FUNCTION_PANEL_CONTROL        0x82
 #define ATOM_BIOS_FUNCTION_OLD_DEVICE_DET       0x83
 #define ATOM_BIOS_FUNCTION_OLD_DEVICE_SWITCH    0x84
-#define ATOM_BIOS_FUNCTION_HW_ICON              0x8A
+#define ATOM_BIOS_FUNCTION_HW_ICON              0x8A 
 #define ATOM_BIOS_FUNCTION_SET_CMOS             0x8B
-#define SUB_FUNCTION_UPDATE_DISPLAY_INFO        0x8000 /*  Sub function 80 */
-#define SUB_FUNCTION_UPDATE_EXPANSION_INFO      0x8100 /*  Sub function 80 */
+#define SUB_FUNCTION_UPDATE_DISPLAY_INFO        0x8000          // Sub function 80
+#define SUB_FUNCTION_UPDATE_EXPANSION_INFO      0x8100          // Sub function 80
 
 #define ATOM_BIOS_FUNCTION_DISPLAY_INFO         0x8D
 #define ATOM_BIOS_FUNCTION_DEVICE_ON_OFF        0x8E
-#define ATOM_BIOS_FUNCTION_VIDEO_STATE          0x8F
-#define ATOM_SUB_FUNCTION_GET_CRITICAL_STATE    0x0300 /*  Sub function 03 */
-#define ATOM_SUB_FUNCTION_GET_LIDSTATE          0x0700 /*  Sub function 7 */
-#define ATOM_SUB_FUNCTION_THERMAL_STATE_NOTICE  0x1400 /*  Notify caller the current thermal state */
-#define ATOM_SUB_FUNCTION_CRITICAL_STATE_NOTICE 0x8300 /*  Notify caller the current critical state */
-#define ATOM_SUB_FUNCTION_SET_LIDSTATE          0x8500 /*  Sub function 85 */
-#define ATOM_SUB_FUNCTION_GET_REQ_DISPLAY_FROM_SBIOS_MODE 0x8900       /*  Sub function 89 */
-#define ATOM_SUB_FUNCTION_INFORM_ADC_SUPPORT    0x9400 /*  Notify caller that ADC is supported */
-
-#define ATOM_BIOS_FUNCTION_VESA_DPMS            0x4F10 /*  Set DPMS */
-#define ATOM_SUB_FUNCTION_SET_DPMS              0x0001 /*  BL: Sub function 01 */
-#define ATOM_SUB_FUNCTION_GET_DPMS              0x0002 /*  BL: Sub function 02 */
-#define ATOM_PARAMETER_VESA_DPMS_ON             0x0000 /*  BH Parameter for DPMS ON. */
-#define ATOM_PARAMETER_VESA_DPMS_STANDBY        0x0100 /*  BH Parameter for DPMS STANDBY */
-#define ATOM_PARAMETER_VESA_DPMS_SUSPEND        0x0200 /*  BH Parameter for DPMS SUSPEND */
-#define ATOM_PARAMETER_VESA_DPMS_OFF            0x0400 /*  BH Parameter for DPMS OFF */
-#define ATOM_PARAMETER_VESA_DPMS_REDUCE_ON      0x0800 /*  BH Parameter for DPMS REDUCE ON (NOT SUPPORTED) */
+#define ATOM_BIOS_FUNCTION_VIDEO_STATE          0x8F 
+#define ATOM_SUB_FUNCTION_GET_CRITICAL_STATE    0x0300          // Sub function 03  
+#define ATOM_SUB_FUNCTION_GET_LIDSTATE          0x0700          // Sub function 7
+#define ATOM_SUB_FUNCTION_THERMAL_STATE_NOTICE  0x1400          // Notify caller the current thermal state
+#define ATOM_SUB_FUNCTION_CRITICAL_STATE_NOTICE 0x8300          // Notify caller the current critical state
+#define ATOM_SUB_FUNCTION_SET_LIDSTATE          0x8500          // Sub function 85
+#define ATOM_SUB_FUNCTION_GET_REQ_DISPLAY_FROM_SBIOS_MODE 0x8900// Sub function 89
+#define ATOM_SUB_FUNCTION_INFORM_ADC_SUPPORT    0x9400          // Notify caller that ADC is supported
+     
+
+#define ATOM_BIOS_FUNCTION_VESA_DPMS            0x4F10          // Set DPMS 
+#define ATOM_SUB_FUNCTION_SET_DPMS              0x0001          // BL: Sub function 01 
+#define ATOM_SUB_FUNCTION_GET_DPMS              0x0002          // BL: Sub function 02 
+#define ATOM_PARAMETER_VESA_DPMS_ON             0x0000          // BH Parameter for DPMS ON.  
+#define ATOM_PARAMETER_VESA_DPMS_STANDBY        0x0100          // BH Parameter for DPMS STANDBY  
+#define ATOM_PARAMETER_VESA_DPMS_SUSPEND        0x0200          // BH Parameter for DPMS SUSPEND
+#define ATOM_PARAMETER_VESA_DPMS_OFF            0x0400          // BH Parameter for DPMS OFF
+#define ATOM_PARAMETER_VESA_DPMS_REDUCE_ON      0x0800          // BH Parameter for DPMS REDUCE ON (NOT SUPPORTED)
 
 #define ATOM_BIOS_RETURN_CODE_MASK              0x0000FF00L
 #define ATOM_BIOS_REG_HIGH_MASK                 0x0000FF00L
 #define ATOM_BIOS_REG_LOW_MASK                  0x000000FFL
 
-/*  structure used for VBIOS only */
+// structure used for VBIOS only
 
-/* DispOutInfoTable */
-typedef struct _ASIC_TRANSMITTER_INFO {
+//DispOutInfoTable
+typedef struct _ASIC_TRANSMITTER_INFO
+{
        USHORT usTransmitterObjId;
        USHORT usSupportDevice;
-       UCHAR ucTransmitterCmdTblId;
-       UCHAR ucConfig;
-       UCHAR ucEncoderID;      /* available 1st encoder ( default ) */
-       UCHAR ucOptionEncoderID;        /* available 2nd encoder ( optional ) */
-       UCHAR uc2ndEncoderID;
-       UCHAR ucReserved;
-} ASIC_TRANSMITTER_INFO;
-
-typedef struct _ASIC_ENCODER_INFO {
+  UCHAR  ucTransmitterCmdTblId;
+       UCHAR  ucConfig;
+       UCHAR  ucEncoderID;                                      //available 1st encoder ( default )
+       UCHAR  ucOptionEncoderID;    //available 2nd encoder ( optional )
+       UCHAR  uc2ndEncoderID;
+       UCHAR  ucReserved;
+}ASIC_TRANSMITTER_INFO;
+
+typedef struct _ASIC_ENCODER_INFO
+{
        UCHAR ucEncoderID;
        UCHAR ucEncoderConfig;
-       USHORT usEncoderCmdTblId;
-} ASIC_ENCODER_INFO;
+  USHORT usEncoderCmdTblId;
+}ASIC_ENCODER_INFO;
 
-typedef struct _ATOM_DISP_OUT_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
+typedef struct _ATOM_DISP_OUT_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+       USHORT ptrTransmitterInfo;
+       USHORT ptrEncoderInfo;
+       ASIC_TRANSMITTER_INFO  asTransmitterInfo[1];
+       ASIC_ENCODER_INFO      asEncoderInfo[1];
+}ATOM_DISP_OUT_INFO;
+
+typedef struct _ATOM_DISP_OUT_INFO_V2
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
        USHORT ptrTransmitterInfo;
        USHORT ptrEncoderInfo;
-       ASIC_TRANSMITTER_INFO asTransmitterInfo[1];
-       ASIC_ENCODER_INFO asEncoderInfo[1];
-} ATOM_DISP_OUT_INFO;
+  USHORT ptrMainCallParserFar;                  // direct address of main parser call in VBIOS binary. 
+       ASIC_TRANSMITTER_INFO  asTransmitterInfo[1];
+       ASIC_ENCODER_INFO      asEncoderInfo[1];
+}ATOM_DISP_OUT_INFO_V2;
 
-/*  DispDevicePriorityInfo */
-typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
+// DispDevicePriorityInfo
+typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
        USHORT asDevicePriority[16];
-} ATOM_DISPLAY_DEVICE_PRIORITY_INFO;
-
-/* ProcessAuxChannelTransactionTable */
-typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS {
-       USHORT lpAuxRequest;
-       USHORT lpDataOut;
-       UCHAR ucChannelID;
-       union {
-               UCHAR ucReplyStatus;
-               UCHAR ucDelay;
+}ATOM_DISPLAY_DEVICE_PRIORITY_INFO;
+
+//ProcessAuxChannelTransactionTable
+typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS
+{
+       USHORT  lpAuxRequest;
+       USHORT  lpDataOut;
+       UCHAR           ucChannelID;
+       union
+       {
+  UCHAR   ucReplyStatus;
+       UCHAR   ucDelay;
        };
-       UCHAR ucDataOutLen;
-       UCHAR ucReserved;
-} PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS;
+  UCHAR   ucDataOutLen;
+       UCHAR   ucReserved;
+}PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS;
+
+//ProcessAuxChannelTransactionTable
+typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2
+{
+       USHORT  lpAuxRequest;
+       USHORT  lpDataOut;
+       UCHAR           ucChannelID;
+       union
+       {
+  UCHAR   ucReplyStatus;
+       UCHAR   ucDelay;
+       };
+  UCHAR   ucDataOutLen;
+       UCHAR   ucHPD_ID;                                       //=0: HPD1, =1: HPD2, =2: HPD3, =3: HPD4, =4: HPD5, =5: HPD6
+}PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2;
 
 #define PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION                  PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS
 
-/* GetSinkType */
+//GetSinkType
 
-typedef struct _DP_ENCODER_SERVICE_PARAMETERS {
+typedef struct _DP_ENCODER_SERVICE_PARAMETERS
+{
        USHORT ucLinkClock;
-       union {
-               UCHAR ucConfig; /*  for DP training command */
-               UCHAR ucI2cId;  /*  use for GET_SINK_TYPE command */
+       union 
+       {
+       UCHAR ucConfig;                         // for DP training command
+       UCHAR ucI2cId;                          // use for GET_SINK_TYPE command
        };
        UCHAR ucAction;
        UCHAR ucStatus;
        UCHAR ucLaneNum;
        UCHAR ucReserved[2];
-} DP_ENCODER_SERVICE_PARAMETERS;
+}DP_ENCODER_SERVICE_PARAMETERS;
 
-/*  ucAction */
+// ucAction
 #define ATOM_DP_ACTION_GET_SINK_TYPE                                                   0x01
+/* obselete */
 #define ATOM_DP_ACTION_TRAINING_START                                                  0x02
 #define ATOM_DP_ACTION_TRAINING_COMPLETE                                       0x03
 #define ATOM_DP_ACTION_TRAINING_PATTERN_SEL                            0x04
@@ -4318,7 +5273,7 @@ typedef struct _DP_ENCODER_SERVICE_PARAMETERS {
 #define ATOM_DP_ACTION_GET_VSWING_PREEMP                                       0x06
 #define ATOM_DP_ACTION_BLANKING                   0x07
 
-/*  ucConfig */
+// ucConfig
 #define ATOM_DP_CONFIG_ENCODER_SEL_MASK                                                0x03
 #define ATOM_DP_CONFIG_DIG1_ENCODER                                                            0x00
 #define ATOM_DP_CONFIG_DIG2_ENCODER                                                            0x01
@@ -4326,14 +5281,14 @@ typedef struct _DP_ENCODER_SERVICE_PARAMETERS {
 #define ATOM_DP_CONFIG_LINK_SEL_MASK                                                   0x04
 #define ATOM_DP_CONFIG_LINK_A                                                                                  0x00
 #define ATOM_DP_CONFIG_LINK_B                                                                                  0x04
-
+/* /obselete */
 #define DP_ENCODER_SERVICE_PS_ALLOCATION                               WRITE_ONE_BYTE_HW_I2C_DATA_PARAMETERS
 
-/*  DP_TRAINING_TABLE */
-#define DPCD_SET_LINKRATE_LANENUM_PATTERN1_TBL_ADDR                            ATOM_DP_TRAINING_TBL_ADDR
+// DP_TRAINING_TABLE
+#define DPCD_SET_LINKRATE_LANENUM_PATTERN1_TBL_ADDR                            ATOM_DP_TRAINING_TBL_ADDR               
 #define DPCD_SET_SS_CNTL_TBL_ADDR                                                                                                      (ATOM_DP_TRAINING_TBL_ADDR + 8 )
-#define DPCD_SET_LANE_VSWING_PREEMP_TBL_ADDR                                                   (ATOM_DP_TRAINING_TBL_ADDR + 16)
-#define DPCD_SET_TRAINING_PATTERN0_TBL_ADDR                                                            (ATOM_DP_TRAINING_TBL_ADDR + 24)
+#define DPCD_SET_LANE_VSWING_PREEMP_TBL_ADDR                                                   (ATOM_DP_TRAINING_TBL_ADDR + 16 )
+#define DPCD_SET_TRAINING_PATTERN0_TBL_ADDR                                                            (ATOM_DP_TRAINING_TBL_ADDR + 24 )
 #define DPCD_SET_TRAINING_PATTERN2_TBL_ADDR                                                            (ATOM_DP_TRAINING_TBL_ADDR + 32)
 #define DPCD_GET_LINKRATE_LANENUM_SS_TBL_ADDR                                                  (ATOM_DP_TRAINING_TBL_ADDR + 40)
 #define        DPCD_GET_LANE_STATUS_ADJUST_TBL_ADDR                                                    (ATOM_DP_TRAINING_TBL_ADDR + 48)
@@ -4341,183 +5296,241 @@ typedef struct _DP_ENCODER_SERVICE_PARAMETERS {
 #define DP_I2C_AUX_DDC_WRITE_TBL_ADDR                                                                                  (ATOM_DP_TRAINING_TBL_ADDR + 64)
 #define DP_I2C_AUX_DDC_READ_START_TBL_ADDR                                                             (ATOM_DP_TRAINING_TBL_ADDR + 72)
 #define DP_I2C_AUX_DDC_READ_TBL_ADDR                                                                                   (ATOM_DP_TRAINING_TBL_ADDR + 76)
-#define DP_I2C_AUX_DDC_READ_END_TBL_ADDR                                                                       (ATOM_DP_TRAINING_TBL_ADDR + 80)
+#define DP_I2C_AUX_DDC_WRITE_END_TBL_ADDR                 (ATOM_DP_TRAINING_TBL_ADDR + 80) 
+#define DP_I2C_AUX_DDC_READ_END_TBL_ADDR                                                                       (ATOM_DP_TRAINING_TBL_ADDR + 84)
 
-typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS {
-       UCHAR ucI2CSpeed;
-       union {
-               UCHAR ucRegIndex;
-               UCHAR ucStatus;
+typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS
+{
+       UCHAR   ucI2CSpeed;
+       union
+       {
+   UCHAR ucRegIndex;
+   UCHAR ucStatus;
        };
-       USHORT lpI2CDataOut;
-       UCHAR ucFlag;
-       UCHAR ucTransBytes;
-       UCHAR ucSlaveAddr;
-       UCHAR ucLineNumber;
-} PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS;
+       USHORT  lpI2CDataOut;
+  UCHAR   ucFlag;               
+  UCHAR   ucTransBytes;
+  UCHAR   ucSlaveAddr;
+  UCHAR   ucLineNumber;
+}PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS;
 
 #define PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION       PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS
 
-/* ucFlag */
+//ucFlag
 #define HW_I2C_WRITE        1
 #define HW_I2C_READ         0
+#define I2C_2BYTE_ADDR      0x02
 
+typedef struct _SET_HWBLOCK_INSTANCE_PARAMETER_V2
+{
+   UCHAR ucHWBlkInst;                // HW block instance, 0, 1, 2, ...
+   UCHAR ucReserved[3]; 
+}SET_HWBLOCK_INSTANCE_PARAMETER_V2;
+
+#define HWBLKINST_INSTANCE_MASK       0x07
+#define HWBLKINST_HWBLK_MASK          0xF0
+#define HWBLKINST_HWBLK_SHIFT         0x04
+
+//ucHWBlock
+#define SELECT_DISP_ENGINE            0
+#define SELECT_DISP_PLL               1
+#define SELECT_DCIO_UNIPHY_LINK0      2
+#define SELECT_DCIO_UNIPHY_LINK1      3
+#define SELECT_DCIO_IMPCAL            4
+#define SELECT_DCIO_DIG               6
+#define SELECT_CRTC_PIXEL_RATE        7
+
+/****************************************************************************/ 
+//Portion VI: Definitinos for vbios MC scratch registers that driver used
 /****************************************************************************/
-/* Portion VI: Definitinos being oboselete */
+
+#define MC_MISC0__MEMORY_TYPE_MASK    0xF0000000
+#define MC_MISC0__MEMORY_TYPE__GDDR1  0x10000000
+#define MC_MISC0__MEMORY_TYPE__DDR2   0x20000000
+#define MC_MISC0__MEMORY_TYPE__GDDR3  0x30000000
+#define MC_MISC0__MEMORY_TYPE__GDDR4  0x40000000
+#define MC_MISC0__MEMORY_TYPE__GDDR5  0x50000000
+#define MC_MISC0__MEMORY_TYPE__DDR3   0xB0000000
+
+/****************************************************************************/ 
+//Portion VI: Definitinos being oboselete
 /****************************************************************************/
 
-/* ========================================================================================== */
-/* Remove the definitions below when driver is ready! */
-typedef struct _ATOM_DAC_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usMaxFrequency;  /*  in 10kHz unit */
-       USHORT usReserved;
-} ATOM_DAC_INFO;
-
-typedef struct _COMPASSIONATE_DATA {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-
-       /* ==============================  DAC1 portion */
-       UCHAR ucDAC1_BG_Adjustment;
-       UCHAR ucDAC1_DAC_Adjustment;
-       USHORT usDAC1_FORCE_Data;
-       /* ==============================  DAC2 portion */
-       UCHAR ucDAC2_CRT2_BG_Adjustment;
-       UCHAR ucDAC2_CRT2_DAC_Adjustment;
-       USHORT usDAC2_CRT2_FORCE_Data;
-       USHORT usDAC2_CRT2_MUX_RegisterIndex;
-       UCHAR ucDAC2_CRT2_MUX_RegisterInfo;     /* Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low */
-       UCHAR ucDAC2_NTSC_BG_Adjustment;
-       UCHAR ucDAC2_NTSC_DAC_Adjustment;
-       USHORT usDAC2_TV1_FORCE_Data;
-       USHORT usDAC2_TV1_MUX_RegisterIndex;
-       UCHAR ucDAC2_TV1_MUX_RegisterInfo;      /* Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low */
-       UCHAR ucDAC2_CV_BG_Adjustment;
-       UCHAR ucDAC2_CV_DAC_Adjustment;
-       USHORT usDAC2_CV_FORCE_Data;
-       USHORT usDAC2_CV_MUX_RegisterIndex;
-       UCHAR ucDAC2_CV_MUX_RegisterInfo;       /* Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low */
-       UCHAR ucDAC2_PAL_BG_Adjustment;
-       UCHAR ucDAC2_PAL_DAC_Adjustment;
-       USHORT usDAC2_TV2_FORCE_Data;
-} COMPASSIONATE_DATA;
+//==========================================================================================
+//Remove the definitions below when driver is ready!
+typedef struct _ATOM_DAC_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  USHORT                   usMaxFrequency;      // in 10kHz unit
+  USHORT                   usReserved;
+}ATOM_DAC_INFO;
+
+
+typedef struct  _COMPASSIONATE_DATA           
+{
+  ATOM_COMMON_TABLE_HEADER sHeader; 
+
+  //==============================  DAC1 portion
+  UCHAR   ucDAC1_BG_Adjustment;
+  UCHAR   ucDAC1_DAC_Adjustment;
+  USHORT  usDAC1_FORCE_Data;
+  //==============================  DAC2 portion
+  UCHAR   ucDAC2_CRT2_BG_Adjustment;
+  UCHAR   ucDAC2_CRT2_DAC_Adjustment;
+  USHORT  usDAC2_CRT2_FORCE_Data;
+  USHORT  usDAC2_CRT2_MUX_RegisterIndex;
+  UCHAR   ucDAC2_CRT2_MUX_RegisterInfo;     //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low
+  UCHAR   ucDAC2_NTSC_BG_Adjustment;
+  UCHAR   ucDAC2_NTSC_DAC_Adjustment;
+  USHORT  usDAC2_TV1_FORCE_Data;
+  USHORT  usDAC2_TV1_MUX_RegisterIndex;
+  UCHAR   ucDAC2_TV1_MUX_RegisterInfo;      //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low
+  UCHAR   ucDAC2_CV_BG_Adjustment;
+  UCHAR   ucDAC2_CV_DAC_Adjustment;
+  USHORT  usDAC2_CV_FORCE_Data;
+  USHORT  usDAC2_CV_MUX_RegisterIndex;
+  UCHAR   ucDAC2_CV_MUX_RegisterInfo;       //Bit[4:0]=Bit position,Bit[7]=1:Active High;=0 Active Low
+  UCHAR   ucDAC2_PAL_BG_Adjustment;
+  UCHAR   ucDAC2_PAL_DAC_Adjustment;
+  USHORT  usDAC2_TV2_FORCE_Data;
+}COMPASSIONATE_DATA;
 
 /****************************Supported Device Info Table Definitions**********************/
-/*   ucConnectInfo: */
-/*     [7:4] - connector type */
-/*       = 1   - VGA connector */
-/*       = 2   - DVI-I */
-/*       = 3   - DVI-D */
-/*       = 4   - DVI-A */
-/*       = 5   - SVIDEO */
-/*       = 6   - COMPOSITE */
-/*       = 7   - LVDS */
-/*       = 8   - DIGITAL LINK */
-/*       = 9   - SCART */
-/*       = 0xA - HDMI_type A */
-/*       = 0xB - HDMI_type B */
-/*       = 0xE - Special case1 (DVI+DIN) */
-/*       Others=TBD */
-/*     [3:0] - DAC Associated */
-/*       = 0   - no DAC */
-/*       = 1   - DACA */
-/*       = 2   - DACB */
-/*       = 3   - External DAC */
-/*       Others=TBD */
-/*  */
-
-typedef struct _ATOM_CONNECTOR_INFO {
+//  ucConnectInfo:
+//    [7:4] - connector type
+//      = 1   - VGA connector   
+//      = 2   - DVI-I
+//      = 3   - DVI-D
+//      = 4   - DVI-A
+//      = 5   - SVIDEO
+//      = 6   - COMPOSITE
+//      = 7   - LVDS
+//      = 8   - DIGITAL LINK
+//      = 9   - SCART
+//      = 0xA - HDMI_type A
+//      = 0xB - HDMI_type B
+//      = 0xE - Special case1 (DVI+DIN)
+//      Others=TBD
+//    [3:0] - DAC Associated
+//      = 0   - no DAC
+//      = 1   - DACA
+//      = 2   - DACB
+//      = 3   - External DAC
+//      Others=TBD
+//    
+
+typedef struct _ATOM_CONNECTOR_INFO
+{
 #if ATOM_BIG_ENDIAN
-       UCHAR bfConnectorType:4;
-       UCHAR bfAssociatedDAC:4;
+  UCHAR   bfConnectorType:4;
+  UCHAR   bfAssociatedDAC:4;
 #else
-       UCHAR bfAssociatedDAC:4;
-       UCHAR bfConnectorType:4;
+  UCHAR   bfAssociatedDAC:4;
+  UCHAR   bfConnectorType:4;
 #endif
-} ATOM_CONNECTOR_INFO;
+}ATOM_CONNECTOR_INFO;
+
+typedef union _ATOM_CONNECTOR_INFO_ACCESS
+{
+  ATOM_CONNECTOR_INFO sbfAccess;
+  UCHAR               ucAccess;
+}ATOM_CONNECTOR_INFO_ACCESS;
 
-typedef union _ATOM_CONNECTOR_INFO_ACCESS {
-       ATOM_CONNECTOR_INFO sbfAccess;
-       UCHAR ucAccess;
-} ATOM_CONNECTOR_INFO_ACCESS;
+typedef struct _ATOM_CONNECTOR_INFO_I2C
+{
+  ATOM_CONNECTOR_INFO_ACCESS sucConnectorInfo;
+  ATOM_I2C_ID_CONFIG_ACCESS  sucI2cId;
+}ATOM_CONNECTOR_INFO_I2C;
 
-typedef struct _ATOM_CONNECTOR_INFO_I2C {
-       ATOM_CONNECTOR_INFO_ACCESS sucConnectorInfo;
-       ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;
-} ATOM_CONNECTOR_INFO_I2C;
 
-typedef struct _ATOM_SUPPORTED_DEVICES_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usDeviceSupport;
-       ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO];
-} ATOM_SUPPORTED_DEVICES_INFO;
+typedef struct _ATOM_SUPPORTED_DEVICES_INFO
+{ 
+  ATOM_COMMON_TABLE_HEADER     sHeader;
+  USHORT                    usDeviceSupport;
+  ATOM_CONNECTOR_INFO_I2C   asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO];
+}ATOM_SUPPORTED_DEVICES_INFO;
 
 #define NO_INT_SRC_MAPPED       0xFF
 
-typedef struct _ATOM_CONNECTOR_INC_SRC_BITMAP {
-       UCHAR ucIntSrcBitmap;
-} ATOM_CONNECTOR_INC_SRC_BITMAP;
-
-typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usDeviceSupport;
-       ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2];
-       ATOM_CONNECTOR_INC_SRC_BITMAP
-           asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2];
-} ATOM_SUPPORTED_DEVICES_INFO_2;
-
-typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usDeviceSupport;
-       ATOM_CONNECTOR_INFO_I2C asConnInfo[ATOM_MAX_SUPPORTED_DEVICE];
-       ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE];
-} ATOM_SUPPORTED_DEVICES_INFO_2d1;
+typedef struct _ATOM_CONNECTOR_INC_SRC_BITMAP
+{
+  UCHAR   ucIntSrcBitmap;
+}ATOM_CONNECTOR_INC_SRC_BITMAP;
+
+typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2
+{ 
+  ATOM_COMMON_TABLE_HEADER      sHeader;
+  USHORT                        usDeviceSupport;
+  ATOM_CONNECTOR_INFO_I2C       asConnInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2];
+  ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE_INFO_2];
+}ATOM_SUPPORTED_DEVICES_INFO_2;
+
+typedef struct _ATOM_SUPPORTED_DEVICES_INFO_2d1
+{ 
+  ATOM_COMMON_TABLE_HEADER      sHeader;
+  USHORT                        usDeviceSupport;
+  ATOM_CONNECTOR_INFO_I2C       asConnInfo[ATOM_MAX_SUPPORTED_DEVICE];
+  ATOM_CONNECTOR_INC_SRC_BITMAP asIntSrcInfo[ATOM_MAX_SUPPORTED_DEVICE];
+}ATOM_SUPPORTED_DEVICES_INFO_2d1;
 
 #define ATOM_SUPPORTED_DEVICES_INFO_LAST ATOM_SUPPORTED_DEVICES_INFO_2d1
 
-typedef struct _ATOM_MISC_CONTROL_INFO {
-       USHORT usFrequency;
-       UCHAR ucPLL_ChargePump; /*  PLL charge-pump gain control */
-       UCHAR ucPLL_DutyCycle;  /*  PLL duty cycle control */
-       UCHAR ucPLL_VCO_Gain;   /*  PLL VCO gain control */
-       UCHAR ucPLL_VoltageSwing;       /*  PLL driver voltage swing control */
-} ATOM_MISC_CONTROL_INFO;
+
+
+typedef struct _ATOM_MISC_CONTROL_INFO
+{
+   USHORT usFrequency;
+   UCHAR  ucPLL_ChargePump;                                            // PLL charge-pump gain control
+   UCHAR  ucPLL_DutyCycle;                                             // PLL duty cycle control
+   UCHAR  ucPLL_VCO_Gain;                                                // PLL VCO gain control
+   UCHAR  ucPLL_VoltageSwing;                                  // PLL driver voltage swing control
+}ATOM_MISC_CONTROL_INFO;  
+
 
 #define ATOM_MAX_MISC_INFO       4
 
-typedef struct _ATOM_TMDS_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usMaxFrequency;  /*  in 10Khz */
-       ATOM_MISC_CONTROL_INFO asMiscInfo[ATOM_MAX_MISC_INFO];
-} ATOM_TMDS_INFO;
+typedef struct _ATOM_TMDS_INFO
+{
+  ATOM_COMMON_TABLE_HEADER sHeader;  
+  USHORT                                                       usMaxFrequency;             // in 10Khz
+  ATOM_MISC_CONTROL_INFO                               asMiscInfo[ATOM_MAX_MISC_INFO];
+}ATOM_TMDS_INFO;
+
 
-typedef struct _ATOM_ENCODER_ANALOG_ATTRIBUTE {
-       UCHAR ucTVStandard;     /* Same as TV standards defined above, */
-       UCHAR ucPadding[1];
-} ATOM_ENCODER_ANALOG_ATTRIBUTE;
+typedef struct _ATOM_ENCODER_ANALOG_ATTRIBUTE
+{
+  UCHAR ucTVStandard;     //Same as TV standards defined above, 
+  UCHAR ucPadding[1];
+}ATOM_ENCODER_ANALOG_ATTRIBUTE;
 
-typedef struct _ATOM_ENCODER_DIGITAL_ATTRIBUTE {
-       UCHAR ucAttribute;      /* Same as other digital encoder attributes defined above */
-       UCHAR ucPadding[1];
-} ATOM_ENCODER_DIGITAL_ATTRIBUTE;
+typedef struct _ATOM_ENCODER_DIGITAL_ATTRIBUTE
+{
+  UCHAR ucAttribute;      //Same as other digital encoder attributes defined above
+  UCHAR ucPadding[1];          
+}ATOM_ENCODER_DIGITAL_ATTRIBUTE;
 
-typedef union _ATOM_ENCODER_ATTRIBUTE {
-       ATOM_ENCODER_ANALOG_ATTRIBUTE sAlgAttrib;
-       ATOM_ENCODER_DIGITAL_ATTRIBUTE sDigAttrib;
-} ATOM_ENCODER_ATTRIBUTE;
+typedef union _ATOM_ENCODER_ATTRIBUTE
+{
+  ATOM_ENCODER_ANALOG_ATTRIBUTE sAlgAttrib;
+  ATOM_ENCODER_DIGITAL_ATTRIBUTE sDigAttrib;
+}ATOM_ENCODER_ATTRIBUTE;
 
-typedef struct _DVO_ENCODER_CONTROL_PARAMETERS {
-       USHORT usPixelClock;
-       USHORT usEncoderID;
-       UCHAR ucDeviceType;     /* Use ATOM_DEVICE_xxx1_Index to indicate device type only. */
-       UCHAR ucAction;         /* ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT */
-       ATOM_ENCODER_ATTRIBUTE usDevAttr;
-} DVO_ENCODER_CONTROL_PARAMETERS;
 
-typedef struct _DVO_ENCODER_CONTROL_PS_ALLOCATION {
-       DVO_ENCODER_CONTROL_PARAMETERS sDVOEncoder;
-       WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;     /* Caller doesn't need to init this portion */
-} DVO_ENCODER_CONTROL_PS_ALLOCATION;
+typedef struct _DVO_ENCODER_CONTROL_PARAMETERS
+{
+  USHORT usPixelClock; 
+  USHORT usEncoderID; 
+  UCHAR  ucDeviceType;                                                                                         //Use ATOM_DEVICE_xxx1_Index to indicate device type only.      
+  UCHAR  ucAction;                                                                                                             //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT
+  ATOM_ENCODER_ATTRIBUTE usDevAttr;                    
+}DVO_ENCODER_CONTROL_PARAMETERS;
+
+typedef struct _DVO_ENCODER_CONTROL_PS_ALLOCATION
+{                               
+  DVO_ENCODER_CONTROL_PARAMETERS    sDVOEncoder;
+  WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION      sReserved;     //Caller doesn't need to init this portion
+}DVO_ENCODER_CONTROL_PS_ALLOCATION;
+
 
 #define ATOM_XTMDS_ASIC_SI164_ID        1
 #define ATOM_XTMDS_ASIC_SI178_ID        2
@@ -4526,27 +5539,30 @@ typedef struct _DVO_ENCODER_CONTROL_PS_ALLOCATION {
 #define ATOM_XTMDS_SUPPORTED_DUALLINK   0x00000002
 #define ATOM_XTMDS_MVPU_FPGA            0x00000004
 
-typedef struct _ATOM_XTMDS_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       USHORT usSingleLinkMaxFrequency;
-       ATOM_I2C_ID_CONFIG_ACCESS sucI2cId;     /* Point the ID on which I2C is used to control external chip */
-       UCHAR ucXtransimitterID;
-       UCHAR ucSupportedLink;  /*  Bit field, bit0=1, single link supported;bit1=1,dual link supported */
-       UCHAR ucSequnceAlterID; /*  Even with the same external TMDS asic, it's possible that the program seqence alters */
-       /*  due to design. This ID is used to alert driver that the sequence is not "standard"! */
-       UCHAR ucMasterAddress;  /*  Address to control Master xTMDS Chip */
-       UCHAR ucSlaveAddress;   /*  Address to control Slave xTMDS Chip */
-} ATOM_XTMDS_INFO;
-
-typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS {
-       UCHAR ucEnable;         /*  ATOM_ENABLE=On or ATOM_DISABLE=Off */
-       UCHAR ucDevice;         /*  ATOM_DEVICE_DFP1_INDEX.... */
-       UCHAR ucPadding[2];
-} DFP_DPMS_STATUS_CHANGE_PARAMETERS;
+                           
+typedef struct _ATOM_XTMDS_INFO
+{
+  ATOM_COMMON_TABLE_HEADER   sHeader;  
+  USHORT                     usSingleLinkMaxFrequency; 
+  ATOM_I2C_ID_CONFIG_ACCESS  sucI2cId;           //Point the ID on which I2C is used to control external chip
+  UCHAR                      ucXtransimitterID;          
+  UCHAR                      ucSupportedLink;    // Bit field, bit0=1, single link supported;bit1=1,dual link supported
+  UCHAR                      ucSequnceAlterID;   // Even with the same external TMDS asic, it's possible that the program seqence alters 
+                                                 // due to design. This ID is used to alert driver that the sequence is not "standard"!              
+  UCHAR                      ucMasterAddress;    // Address to control Master xTMDS Chip
+  UCHAR                      ucSlaveAddress;     // Address to control Slave xTMDS Chip
+}ATOM_XTMDS_INFO;
+
+typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS
+{  
+  UCHAR ucEnable;                     // ATOM_ENABLE=On or ATOM_DISABLE=Off
+  UCHAR ucDevice;                     // ATOM_DEVICE_DFP1_INDEX....
+  UCHAR ucPadding[2];             
+}DFP_DPMS_STATUS_CHANGE_PARAMETERS;
 
 /****************************Legacy Power Play Table Definitions **********************/
 
-/* Definitions for ulPowerPlayMiscInfo */
+//Definitions for ulPowerPlayMiscInfo
 #define ATOM_PM_MISCINFO_SPLIT_CLOCK                     0x00000000L
 #define ATOM_PM_MISCINFO_USING_MCLK_SRC                  0x00000001L
 #define ATOM_PM_MISCINFO_USING_SCLK_SRC                  0x00000002L
@@ -4558,8 +5574,8 @@ typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS {
 
 #define ATOM_PM_MISCINFO_ENGINE_CLOCK_CONTRL_EN          0x00000020L
 #define ATOM_PM_MISCINFO_MEMORY_CLOCK_CONTRL_EN          0x00000040L
-#define ATOM_PM_MISCINFO_PROGRAM_VOLTAGE                 0x00000080L   /* When this bit set, ucVoltageDropIndex is not an index for GPIO pin, but a voltage ID that SW needs program */
-
+#define ATOM_PM_MISCINFO_PROGRAM_VOLTAGE                 0x00000080L  //When this bit set, ucVoltageDropIndex is not an index for GPIO pin, but a voltage ID that SW needs program  
 #define ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN      0x00000100L
 #define ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN         0x00000200L
 #define ATOM_PM_MISCINFO_ASIC_SLEEP_MODE_EN              0x00000400L
@@ -4569,22 +5585,22 @@ typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS {
 #define ATOM_PM_MISCINFO_LOW_LCD_REFRESH_RATE            0x00004000L
 
 #define ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE             0x00008000L
-#define ATOM_PM_MISCINFO_OVER_CLOCK_MODE                 0x00010000L
+#define ATOM_PM_MISCINFO_OVER_CLOCK_MODE                 0x00010000L 
 #define ATOM_PM_MISCINFO_OVER_DRIVE_MODE                 0x00020000L
 #define ATOM_PM_MISCINFO_POWER_SAVING_MODE               0x00040000L
 #define ATOM_PM_MISCINFO_THERMAL_DIODE_MODE              0x00080000L
 
-#define ATOM_PM_MISCINFO_FRAME_MODULATION_MASK           0x00300000L   /* 0-FM Disable, 1-2 level FM, 2-4 level FM, 3-Reserved */
-#define ATOM_PM_MISCINFO_FRAME_MODULATION_SHIFT          20
+#define ATOM_PM_MISCINFO_FRAME_MODULATION_MASK           0x00300000L  //0-FM Disable, 1-2 level FM, 2-4 level FM, 3-Reserved
+#define ATOM_PM_MISCINFO_FRAME_MODULATION_SHIFT          20 
 
 #define ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE                 0x00400000L
 #define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2      0x00800000L
 #define ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4      0x01000000L
-#define ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN            0x02000000L   /* When set, Dynamic */
-#define ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN        0x04000000L   /* When set, Dynamic */
-#define ATOM_PM_MISCINFO_3D_ACCELERATION_EN              0x08000000L   /* When set, This mode is for acceleated 3D mode */
+#define ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN            0x02000000L  //When set, Dynamic 
+#define ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN        0x04000000L  //When set, Dynamic
+#define ATOM_PM_MISCINFO_3D_ACCELERATION_EN              0x08000000L  //When set, This mode is for acceleated 3D mode
 
-#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_MASK   0x70000000L   /* 1-Optimal Battery Life Group, 2-High Battery, 3-Balanced, 4-High Performance, 5- Optimal Performance (Default state with Default clocks) */
+#define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_MASK   0x70000000L  //1-Optimal Battery Life Group, 2-High Battery, 3-Balanced, 4-High Performance, 5- Optimal Performance (Default state with Default clocks) 
 #define ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_SHIFT  28
 #define ATOM_PM_MISCINFO_ENABLE_BACK_BIAS                0x80000000L
 
@@ -4594,55 +5610,59 @@ typedef struct _DFP_DPMS_STATUS_CHANGE_PARAMETERS {
 #define ATOM_PM_MISCINFO2_FS3D_OVERDRIVE_INFO            0x00000008L
 #define ATOM_PM_MISCINFO2_FORCEDLOWPWR_MODE              0x00000010L
 #define ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN       0x00000020L
-#define ATOM_PM_MISCINFO2_VIDEO_PLAYBACK_CAPABLE         0x00000040L   /* If this bit is set in multi-pp mode, then driver will pack up one with the minior power consumption. */
-                                                                     /* If it's not set in any pp mode, driver will use its default logic to pick a pp mode in video playback */
+#define ATOM_PM_MISCINFO2_VIDEO_PLAYBACK_CAPABLE         0x00000040L  //If this bit is set in multi-pp mode, then driver will pack up one with the minior power consumption. 
+                                                                      //If it's not set in any pp mode, driver will use its default logic to pick a pp mode in video playback
 #define ATOM_PM_MISCINFO2_NOT_VALID_ON_DC                0x00000080L
 #define ATOM_PM_MISCINFO2_STUTTER_MODE_EN                0x00000100L
-#define ATOM_PM_MISCINFO2_UVD_SUPPORT_MODE               0x00000200L
-
-/* ucTableFormatRevision=1 */
-/* ucTableContentRevision=1 */
-typedef struct _ATOM_POWERMODE_INFO {
-       ULONG ulMiscInfo;       /* The power level should be arranged in ascending order */
-       ULONG ulReserved1;      /*  must set to 0 */
-       ULONG ulReserved2;      /*  must set to 0 */
-       USHORT usEngineClock;
-       USHORT usMemoryClock;
-       UCHAR ucVoltageDropIndex;       /*  index to GPIO table */
-       UCHAR ucSelectedPanel_RefreshRate;      /*  panel refresh rate */
-       UCHAR ucMinTemperature;
-       UCHAR ucMaxTemperature;
-       UCHAR ucNumPciELanes;   /*  number of PCIE lanes */
-} ATOM_POWERMODE_INFO;
-
-/* ucTableFormatRevision=2 */
-/* ucTableContentRevision=1 */
-typedef struct _ATOM_POWERMODE_INFO_V2 {
-       ULONG ulMiscInfo;       /* The power level should be arranged in ascending order */
-       ULONG ulMiscInfo2;
-       ULONG ulEngineClock;
-       ULONG ulMemoryClock;
-       UCHAR ucVoltageDropIndex;       /*  index to GPIO table */
-       UCHAR ucSelectedPanel_RefreshRate;      /*  panel refresh rate */
-       UCHAR ucMinTemperature;
-       UCHAR ucMaxTemperature;
-       UCHAR ucNumPciELanes;   /*  number of PCIE lanes */
-} ATOM_POWERMODE_INFO_V2;
-
-/* ucTableFormatRevision=2 */
-/* ucTableContentRevision=2 */
-typedef struct _ATOM_POWERMODE_INFO_V3 {
-       ULONG ulMiscInfo;       /* The power level should be arranged in ascending order */
-       ULONG ulMiscInfo2;
-       ULONG ulEngineClock;
-       ULONG ulMemoryClock;
-       UCHAR ucVoltageDropIndex;       /*  index to Core (VDDC) votage table */
-       UCHAR ucSelectedPanel_RefreshRate;      /*  panel refresh rate */
-       UCHAR ucMinTemperature;
-       UCHAR ucMaxTemperature;
-       UCHAR ucNumPciELanes;   /*  number of PCIE lanes */
-       UCHAR ucVDDCI_VoltageDropIndex; /*  index to VDDCI votage table */
-} ATOM_POWERMODE_INFO_V3;
+#define ATOM_PM_MISCINFO2_UVD_SUPPORT_MODE               0x00000200L 
+
+//ucTableFormatRevision=1
+//ucTableContentRevision=1
+typedef struct  _ATOM_POWERMODE_INFO
+{
+  ULONG     ulMiscInfo;                 //The power level should be arranged in ascending order
+  ULONG     ulReserved1;                // must set to 0
+  ULONG     ulReserved2;                // must set to 0
+  USHORT    usEngineClock;
+  USHORT    usMemoryClock;
+  UCHAR     ucVoltageDropIndex;         // index to GPIO table
+  UCHAR     ucSelectedPanel_RefreshRate;// panel refresh rate
+  UCHAR     ucMinTemperature;
+  UCHAR     ucMaxTemperature;
+  UCHAR     ucNumPciELanes;             // number of PCIE lanes
+}ATOM_POWERMODE_INFO;
+
+//ucTableFormatRevision=2
+//ucTableContentRevision=1
+typedef struct  _ATOM_POWERMODE_INFO_V2
+{
+  ULONG     ulMiscInfo;                 //The power level should be arranged in ascending order
+  ULONG     ulMiscInfo2;                
+  ULONG     ulEngineClock;                
+  ULONG     ulMemoryClock;
+  UCHAR     ucVoltageDropIndex;         // index to GPIO table
+  UCHAR     ucSelectedPanel_RefreshRate;// panel refresh rate
+  UCHAR     ucMinTemperature;
+  UCHAR     ucMaxTemperature;
+  UCHAR     ucNumPciELanes;             // number of PCIE lanes
+}ATOM_POWERMODE_INFO_V2;
+
+//ucTableFormatRevision=2
+//ucTableContentRevision=2
+typedef struct  _ATOM_POWERMODE_INFO_V3
+{
+  ULONG     ulMiscInfo;                 //The power level should be arranged in ascending order
+  ULONG     ulMiscInfo2;                
+  ULONG     ulEngineClock;                
+  ULONG     ulMemoryClock;
+  UCHAR     ucVoltageDropIndex;         // index to Core (VDDC) votage table
+  UCHAR     ucSelectedPanel_RefreshRate;// panel refresh rate
+  UCHAR     ucMinTemperature;
+  UCHAR     ucMaxTemperature;
+  UCHAR     ucNumPciELanes;             // number of PCIE lanes
+  UCHAR     ucVDDCI_VoltageDropIndex;   // index to VDDCI votage table
+}ATOM_POWERMODE_INFO_V3;
+
 
 #define ATOM_MAX_NUMBEROF_POWER_BLOCK  8
 
@@ -4655,40 +5675,44 @@ typedef struct _ATOM_POWERMODE_INFO_V3 {
 #define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_MUA6649   0x04
 #define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_LM64      0x05
 #define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_F75375    0x06
-#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ASC7512   0x07     /*  Andigilog */
-
-typedef struct _ATOM_POWERPLAY_INFO {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucOverdriveThermalController;
-       UCHAR ucOverdriveI2cLine;
-       UCHAR ucOverdriveIntBitmap;
-       UCHAR ucOverdriveControllerAddress;
-       UCHAR ucSizeOfPowerModeEntry;
-       UCHAR ucNumOfPowerModeEntries;
-       ATOM_POWERMODE_INFO asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
-} ATOM_POWERPLAY_INFO;
-
-typedef struct _ATOM_POWERPLAY_INFO_V2 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucOverdriveThermalController;
-       UCHAR ucOverdriveI2cLine;
-       UCHAR ucOverdriveIntBitmap;
-       UCHAR ucOverdriveControllerAddress;
-       UCHAR ucSizeOfPowerModeEntry;
-       UCHAR ucNumOfPowerModeEntries;
-       ATOM_POWERMODE_INFO_V2 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
-} ATOM_POWERPLAY_INFO_V2;
-
-typedef struct _ATOM_POWERPLAY_INFO_V3 {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-       UCHAR ucOverdriveThermalController;
-       UCHAR ucOverdriveI2cLine;
-       UCHAR ucOverdriveIntBitmap;
-       UCHAR ucOverdriveControllerAddress;
-       UCHAR ucSizeOfPowerModeEntry;
-       UCHAR ucNumOfPowerModeEntries;
-       ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
-} ATOM_POWERPLAY_INFO_V3;
+#define ATOM_PP_OVERDRIVE_THERMALCONTROLLER_ASC7512   0x07     // Andigilog
+
+
+typedef struct  _ATOM_POWERPLAY_INFO
+{
+  ATOM_COMMON_TABLE_HEADER     sHeader; 
+  UCHAR    ucOverdriveThermalController;
+  UCHAR    ucOverdriveI2cLine;
+  UCHAR    ucOverdriveIntBitmap;
+  UCHAR    ucOverdriveControllerAddress;
+  UCHAR    ucSizeOfPowerModeEntry;
+  UCHAR    ucNumOfPowerModeEntries;
+  ATOM_POWERMODE_INFO asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
+}ATOM_POWERPLAY_INFO;
+
+typedef struct  _ATOM_POWERPLAY_INFO_V2
+{
+  ATOM_COMMON_TABLE_HEADER     sHeader; 
+  UCHAR    ucOverdriveThermalController;
+  UCHAR    ucOverdriveI2cLine;
+  UCHAR    ucOverdriveIntBitmap;
+  UCHAR    ucOverdriveControllerAddress;
+  UCHAR    ucSizeOfPowerModeEntry;
+  UCHAR    ucNumOfPowerModeEntries;
+  ATOM_POWERMODE_INFO_V2 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
+}ATOM_POWERPLAY_INFO_V2;
+  
+typedef struct  _ATOM_POWERPLAY_INFO_V3
+{
+  ATOM_COMMON_TABLE_HEADER     sHeader; 
+  UCHAR    ucOverdriveThermalController;
+  UCHAR    ucOverdriveI2cLine;
+  UCHAR    ucOverdriveIntBitmap;
+  UCHAR    ucOverdriveControllerAddress;
+  UCHAR    ucSizeOfPowerModeEntry;
+  UCHAR    ucNumOfPowerModeEntries;
+  ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
+}ATOM_POWERPLAY_INFO_V3;
 
 /* New PPlib */
 /**************************************************************************/
@@ -4873,40 +5897,42 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
       UCHAR  ucMaxHTLinkWidth;            // From SBIOS - {2, 4, 8, 16}
       UCHAR  ucMinHTLinkWidth;            // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement.
       USHORT usHTLinkFreq;                // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200).
-      ULONG  ulFlags;
+      ULONG  ulFlags; 
 } ATOM_PPLIB_RS780_CLOCK_INFO;
 
-#define ATOM_PPLIB_RS780_VOLTAGE_NONE       0
-#define ATOM_PPLIB_RS780_VOLTAGE_LOW        1
-#define ATOM_PPLIB_RS780_VOLTAGE_HIGH       2
-#define ATOM_PPLIB_RS780_VOLTAGE_VARIABLE   3
+#define ATOM_PPLIB_RS780_VOLTAGE_NONE       0 
+#define ATOM_PPLIB_RS780_VOLTAGE_LOW        1 
+#define ATOM_PPLIB_RS780_VOLTAGE_HIGH       2 
+#define ATOM_PPLIB_RS780_VOLTAGE_VARIABLE   3 
 
 #define ATOM_PPLIB_RS780_SPMCLK_NONE        0   // We cannot change the side port memory clock, leave it as it is.
 #define ATOM_PPLIB_RS780_SPMCLK_LOW         1
 #define ATOM_PPLIB_RS780_SPMCLK_HIGH        2
 
-#define ATOM_PPLIB_RS780_HTLINKFREQ_NONE       0
-#define ATOM_PPLIB_RS780_HTLINKFREQ_LOW        1
-#define ATOM_PPLIB_RS780_HTLINKFREQ_HIGH       2
+#define ATOM_PPLIB_RS780_HTLINKFREQ_NONE       0 
+#define ATOM_PPLIB_RS780_HTLINKFREQ_LOW        1 
+#define ATOM_PPLIB_RS780_HTLINKFREQ_HIGH       2 
 
 /**************************************************************************/
 
-/*  Following definitions are for compatiblity issue in different SW components. */
+
+// Following definitions are for compatiblity issue in different SW components. 
 #define ATOM_MASTER_DATA_TABLE_REVISION   0x01
-#define Object_Info                                                                                            Object_Header
+#define Object_Info                                                                                            Object_Header                   
 #define        AdjustARB_SEQ                                                                                   MC_InitParameter
 #define        VRAM_GPIO_DetectionInfo                                         VoltageObjectInfo
-#define        ASIC_VDDCI_Info                   ASIC_ProfilingInfo
+#define        ASIC_VDDCI_Info                   ASIC_ProfilingInfo                                                                                                            
 #define ASIC_MVDDQ_Info                                                                                MemoryTrainingInfo
-#define SS_Info                           PPLL_SS_Info
+#define SS_Info                           PPLL_SS_Info                      
 #define ASIC_MVDDC_Info                   ASIC_InternalSS_Info
 #define DispDevicePriorityInfo                                         SaveRestoreInfo
 #define DispOutInfo                                                                                            TV_VideoMode
 
+
 #define ATOM_ENCODER_OBJECT_TABLE         ATOM_OBJECT_TABLE
 #define ATOM_CONNECTOR_OBJECT_TABLE       ATOM_OBJECT_TABLE
 
-/* New device naming, remove them when both DAL/VBIOS is ready */
+//New device naming, remove them when both DAL/VBIOS is ready
 #define DFP2I_OUTPUT_CONTROL_PARAMETERS    CRT1_OUTPUT_CONTROL_PARAMETERS
 #define DFP2I_OUTPUT_CONTROL_PS_ALLOCATION DFP2I_OUTPUT_CONTROL_PARAMETERS
 
@@ -4921,7 +5947,7 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
 
 #define ATOM_DEVICE_DFP1I_INDEX            ATOM_DEVICE_DFP1_INDEX
 #define ATOM_DEVICE_DFP1X_INDEX            ATOM_DEVICE_DFP2_INDEX
-
 #define ATOM_DEVICE_DFP2I_INDEX            0x00000009
 #define ATOM_DEVICE_DFP2I_SUPPORT          (0x1L << ATOM_DEVICE_DFP2I_INDEX)
 
@@ -4939,7 +5965,7 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
 
 #define ATOM_S3_DFP2I_ACTIVEb1             0x02
 
-#define ATOM_S3_DFP1I_ACTIVE               ATOM_S3_DFP1_ACTIVE
+#define ATOM_S3_DFP1I_ACTIVE               ATOM_S3_DFP1_ACTIVE 
 #define ATOM_S3_DFP1X_ACTIVE               ATOM_S3_DFP2_ACTIVE
 
 #define ATOM_S3_DFP2I_ACTIVE               0x00000200L
@@ -4958,14 +5984,14 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
 #define ATOM_S6_ACC_REQ_DFP2Ib3            0x02
 #define ATOM_S6_ACC_REQ_DFP2I              0x02000000L
 
-#define TMDS1XEncoderControl               DVOEncoderControl
+#define TMDS1XEncoderControl               DVOEncoderControl           
 #define DFP1XOutputControl                 DVOOutputControl
 
 #define ExternalDFPOutputControl           DFP1XOutputControl
 #define EnableExternalTMDS_Encoder         TMDS1XEncoderControl
 
 #define DFP1IOutputControl                 TMDSAOutputControl
-#define DFP2IOutputControl                 LVTMAOutputControl
+#define DFP2IOutputControl                 LVTMAOutputControl      
 
 #define DAC1_ENCODER_CONTROL_PARAMETERS    DAC_ENCODER_CONTROL_PARAMETERS
 #define DAC1_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION
@@ -4974,7 +6000,7 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
 #define DAC2_ENCODER_CONTROL_PS_ALLOCATION DAC_ENCODER_CONTROL_PS_ALLOCATION
 
 #define ucDac1Standard  ucDacStandard
-#define ucDac2Standard  ucDacStandard
+#define ucDac2Standard  ucDacStandard  
 
 #define TMDS1EncoderControl TMDSAEncoderControl
 #define TMDS2EncoderControl LVTMAEncoderControl
@@ -4984,12 +6010,56 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
 #define CRT1OutputControl   DAC1OutputControl
 #define CRT2OutputControl   DAC2OutputControl
 
-/* These two lines will be removed for sure in a few days, will follow up with Michael V. */
+//These two lines will be removed for sure in a few days, will follow up with Michael V.
 #define EnableLVDS_SS   EnableSpreadSpectrumOnPPLL
-#define ENABLE_LVDS_SS_PARAMETERS_V3  ENABLE_SPREAD_SPECTRUM_ON_PPLL
+#define ENABLE_LVDS_SS_PARAMETERS_V3  ENABLE_SPREAD_SPECTRUM_ON_PPLL  
+
+//#define ATOM_S2_CRT1_DPMS_STATE         0x00010000L
+//#define ATOM_S2_LCD1_DPMS_STATE              ATOM_S2_CRT1_DPMS_STATE
+//#define ATOM_S2_TV1_DPMS_STATE          ATOM_S2_CRT1_DPMS_STATE
+//#define ATOM_S2_DFP1_DPMS_STATE         ATOM_S2_CRT1_DPMS_STATE
+//#define ATOM_S2_CRT2_DPMS_STATE         ATOM_S2_CRT1_DPMS_STATE
+
+#define ATOM_S6_ACC_REQ_TV2             0x00400000L
+#define ATOM_DEVICE_TV2_INDEX           0x00000006
+#define ATOM_DEVICE_TV2_SUPPORT         (0x1L << ATOM_DEVICE_TV2_INDEX)
+#define ATOM_S0_TV2                     0x00100000L
+#define ATOM_S3_TV2_ACTIVE              ATOM_S3_DFP6_ACTIVE
+#define ATOM_S3_TV2_CRTC_ACTIVE         ATOM_S3_DFP6_CRTC_ACTIVE
+
+//
+#define ATOM_S2_CRT1_DPMS_STATE         0x00010000L
+#define ATOM_S2_LCD1_DPMS_STATE                0x00020000L
+#define ATOM_S2_TV1_DPMS_STATE          0x00040000L
+#define ATOM_S2_DFP1_DPMS_STATE         0x00080000L
+#define ATOM_S2_CRT2_DPMS_STATE         0x00100000L
+#define ATOM_S2_LCD2_DPMS_STATE         0x00200000L
+#define ATOM_S2_TV2_DPMS_STATE          0x00400000L
+#define ATOM_S2_DFP2_DPMS_STATE         0x00800000L
+#define ATOM_S2_CV_DPMS_STATE           0x01000000L
+#define ATOM_S2_DFP3_DPMS_STATE                                        0x02000000L
+#define ATOM_S2_DFP4_DPMS_STATE                                        0x04000000L
+#define ATOM_S2_DFP5_DPMS_STATE                                        0x08000000L
+
+#define ATOM_S2_CRT1_DPMS_STATEb2       0x01
+#define ATOM_S2_LCD1_DPMS_STATEb2       0x02
+#define ATOM_S2_TV1_DPMS_STATEb2        0x04
+#define ATOM_S2_DFP1_DPMS_STATEb2       0x08
+#define ATOM_S2_CRT2_DPMS_STATEb2       0x10
+#define ATOM_S2_LCD2_DPMS_STATEb2       0x20
+#define ATOM_S2_TV2_DPMS_STATEb2        0x40
+#define ATOM_S2_DFP2_DPMS_STATEb2       0x80
+#define ATOM_S2_CV_DPMS_STATEb3         0x01
+#define ATOM_S2_DFP3_DPMS_STATEb3                              0x02
+#define ATOM_S2_DFP4_DPMS_STATEb3                              0x04
+#define ATOM_S2_DFP5_DPMS_STATEb3                              0x08
+
+#define ATOM_S3_ASIC_GUI_ENGINE_HUNGb3 0x20
+#define ATOM_S3_ALLOW_FAST_PWR_SWITCHb3 0x40
+#define ATOM_S3_RQST_GPU_USE_MIN_PWRb3  0x80
 
 /*********************************************************************************/
 
-#pragma pack()                 /*  BIOS data must use byte aligment */
+#pragma pack() // BIOS data must use byte aligment
 
 #endif /* _ATOMBIOS_H */
index af464e351fbd95da253435c00b9b3a43c877553c..dd9fdf560611521aeb49d52f90da3fc3a59290df 100644 (file)
@@ -245,21 +245,25 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
-               atombios_enable_crtc(crtc, 1);
+               atombios_enable_crtc(crtc, ATOM_ENABLE);
                if (ASIC_IS_DCE3(rdev))
-                       atombios_enable_crtc_memreq(crtc, 1);
-               atombios_blank_crtc(crtc, 0);
-               drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+                       atombios_enable_crtc_memreq(crtc, ATOM_ENABLE);
+               atombios_blank_crtc(crtc, ATOM_DISABLE);
+               /* XXX re-enable when interrupt support is added */
+               if (!ASIC_IS_DCE4(rdev))
+                       drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
                radeon_crtc_load_lut(crtc);
                break;
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
        case DRM_MODE_DPMS_OFF:
-               drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
-               atombios_blank_crtc(crtc, 1);
+               /* XXX re-enable when interrupt support is added */
+               if (!ASIC_IS_DCE4(rdev))
+                       drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
+               atombios_blank_crtc(crtc, ATOM_ENABLE);
                if (ASIC_IS_DCE3(rdev))
-                       atombios_enable_crtc_memreq(crtc, 0);
-               atombios_enable_crtc(crtc, 0);
+                       atombios_enable_crtc_memreq(crtc, ATOM_DISABLE);
+               atombios_enable_crtc(crtc, ATOM_DISABLE);
                break;
        }
 }
@@ -349,6 +353,11 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc,
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+union atom_enable_ss {
+       ENABLE_LVDS_SS_PARAMETERS legacy;
+       ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1;
+};
+
 static void atombios_set_ss(struct drm_crtc *crtc, int enable)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -358,11 +367,14 @@ static void atombios_set_ss(struct drm_crtc *crtc, int enable)
        struct radeon_encoder *radeon_encoder = NULL;
        struct radeon_encoder_atom_dig *dig = NULL;
        int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
-       ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION args;
-       ENABLE_LVDS_SS_PARAMETERS legacy_args;
+       union atom_enable_ss args;
        uint16_t percentage = 0;
        uint8_t type = 0, step = 0, delay = 0, range = 0;
 
+       /* XXX add ss support for DCE4 */
+       if (ASIC_IS_DCE4(rdev))
+               return;
+
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (encoder->crtc == crtc) {
                        radeon_encoder = to_radeon_encoder(encoder);
@@ -386,29 +398,28 @@ static void atombios_set_ss(struct drm_crtc *crtc, int enable)
        if (!radeon_encoder)
                return;
 
+       memset(&args, 0, sizeof(args));
        if (ASIC_IS_AVIVO(rdev)) {
-               memset(&args, 0, sizeof(args));
-               args.usSpreadSpectrumPercentage = cpu_to_le16(percentage);
-               args.ucSpreadSpectrumType = type;
-               args.ucSpreadSpectrumStep = step;
-               args.ucSpreadSpectrumDelay = delay;
-               args.ucSpreadSpectrumRange = range;
-               args.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
-               args.ucEnable = enable;
-               atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+               args.v1.usSpreadSpectrumPercentage = cpu_to_le16(percentage);
+               args.v1.ucSpreadSpectrumType = type;
+               args.v1.ucSpreadSpectrumStep = step;
+               args.v1.ucSpreadSpectrumDelay = delay;
+               args.v1.ucSpreadSpectrumRange = range;
+               args.v1.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
+               args.v1.ucEnable = enable;
        } else {
-               memset(&legacy_args, 0, sizeof(legacy_args));
-               legacy_args.usSpreadSpectrumPercentage = cpu_to_le16(percentage);
-               legacy_args.ucSpreadSpectrumType = type;
-               legacy_args.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2;
-               legacy_args.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4;
-               legacy_args.ucEnable = enable;
-               atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&legacy_args);
+               args.legacy.usSpreadSpectrumPercentage = cpu_to_le16(percentage);
+               args.legacy.ucSpreadSpectrumType = type;
+               args.legacy.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2;
+               args.legacy.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4;
+               args.legacy.ucEnable = enable;
        }
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
 union adjust_pixel_clock {
        ADJUST_DISPLAY_PLL_PS_ALLOCATION v1;
+       ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3;
 };
 
 static u32 atombios_adjust_pll(struct drm_crtc *crtc,
@@ -420,10 +431,24 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
        struct drm_encoder *encoder = NULL;
        struct radeon_encoder *radeon_encoder = NULL;
        u32 adjusted_clock = mode->clock;
+       int encoder_mode = 0;
 
        /* reset the pll flags */
        pll->flags = 0;
 
+       /* select the PLL algo */
+       if (ASIC_IS_AVIVO(rdev)) {
+               if (radeon_new_pll == 0)
+                       pll->algo = PLL_ALGO_LEGACY;
+               else
+                       pll->algo = PLL_ALGO_NEW;
+       } else {
+               if (radeon_new_pll == 1)
+                       pll->algo = PLL_ALGO_NEW;
+               else
+                       pll->algo = PLL_ALGO_LEGACY;
+       }
+
        if (ASIC_IS_AVIVO(rdev)) {
                if ((rdev->family == CHIP_RS600) ||
                    (rdev->family == CHIP_RS690) ||
@@ -448,10 +473,16 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (encoder->crtc == crtc) {
                        radeon_encoder = to_radeon_encoder(encoder);
+                       encoder_mode = atombios_get_encoder_mode(encoder);
                        if (ASIC_IS_AVIVO(rdev)) {
                                /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
                                if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)
                                        adjusted_clock = mode->clock * 2;
+                               /* LVDS PLL quirks */
+                               if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
+                                       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+                                       pll->algo = dig->pll_algo;
+                               }
                        } else {
                                if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
                                        pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
@@ -468,14 +499,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
         */
        if (ASIC_IS_DCE3(rdev)) {
                union adjust_pixel_clock args;
-               struct radeon_encoder_atom_dig *dig;
                u8 frev, crev;
                int index;
 
-               if (!radeon_encoder->enc_priv)
-                       return adjusted_clock;
-               dig = radeon_encoder->enc_priv;
-
                index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll);
                atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
                                      &crev);
@@ -489,12 +515,51 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                        case 2:
                                args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
                                args.v1.ucTransmitterID = radeon_encoder->encoder_id;
-                               args.v1.ucEncodeMode = atombios_get_encoder_mode(encoder);
+                               args.v1.ucEncodeMode = encoder_mode;
 
                                atom_execute_table(rdev->mode_info.atom_context,
                                                   index, (uint32_t *)&args);
                                adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10;
                                break;
+                       case 3:
+                               args.v3.sInput.usPixelClock = cpu_to_le16(mode->clock / 10);
+                               args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id;
+                               args.v3.sInput.ucEncodeMode = encoder_mode;
+                               args.v3.sInput.ucDispPllConfig = 0;
+                               if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+                                       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+
+                                       if (encoder_mode == ATOM_ENCODER_MODE_DP)
+                                               args.v3.sInput.ucDispPllConfig |=
+                                                       DISPPLL_CONFIG_COHERENT_MODE;
+                                       else {
+                                               if (dig->coherent_mode)
+                                                       args.v3.sInput.ucDispPllConfig |=
+                                                               DISPPLL_CONFIG_COHERENT_MODE;
+                                               if (mode->clock > 165000)
+                                                       args.v3.sInput.ucDispPllConfig |=
+                                                               DISPPLL_CONFIG_DUAL_LINK;
+                                       }
+                               } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+                                       /* may want to enable SS on DP/eDP eventually */
+                                       args.v3.sInput.ucDispPllConfig |=
+                                               DISPPLL_CONFIG_SS_ENABLE;
+                                       if (mode->clock > 165000)
+                                               args.v3.sInput.ucDispPllConfig |=
+                                                       DISPPLL_CONFIG_DUAL_LINK;
+                               }
+                               atom_execute_table(rdev->mode_info.atom_context,
+                                                  index, (uint32_t *)&args);
+                               adjusted_clock = le32_to_cpu(args.v3.sOutput.ulDispPllFreq) * 10;
+                               if (args.v3.sOutput.ucRefDiv) {
+                                       pll->flags |= RADEON_PLL_USE_REF_DIV;
+                                       pll->reference_div = args.v3.sOutput.ucRefDiv;
+                               }
+                               if (args.v3.sOutput.ucPostDiv) {
+                                       pll->flags |= RADEON_PLL_USE_POST_DIV;
+                                       pll->post_div = args.v3.sOutput.ucPostDiv;
+                               }
+                               break;
                        default:
                                DRM_ERROR("Unknown table version %d %d\n", frev, crev);
                                return adjusted_clock;
@@ -513,9 +578,47 @@ union set_pixel_clock {
        PIXEL_CLOCK_PARAMETERS v1;
        PIXEL_CLOCK_PARAMETERS_V2 v2;
        PIXEL_CLOCK_PARAMETERS_V3 v3;
+       PIXEL_CLOCK_PARAMETERS_V5 v5;
 };
 
-void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+static void atombios_crtc_set_dcpll(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       u8 frev, crev;
+       int index;
+       union set_pixel_clock args;
+
+       memset(&args, 0, sizeof(args));
+
+       index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
+       atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
+                             &crev);
+
+       switch (frev) {
+       case 1:
+               switch (crev) {
+               case 5:
+                       /* if the default dcpll clock is specified,
+                        * SetPixelClock provides the dividers
+                        */
+                       args.v5.ucCRTC = ATOM_CRTC_INVALID;
+                       args.v5.usPixelClock = rdev->clock.default_dispclk;
+                       args.v5.ucPpll = ATOM_DCPLL;
+                       break;
+               default:
+                       DRM_ERROR("Unknown table version %d %d\n", frev, crev);
+                       return;
+               }
+               break;
+       default:
+               DRM_ERROR("Unknown table version %d %d\n", frev, crev);
+               return;
+       }
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
+static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct drm_device *dev = crtc->dev;
@@ -529,12 +632,14 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
        u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
        struct radeon_pll *pll;
        u32 adjusted_clock;
+       int encoder_mode = 0;
 
        memset(&args, 0, sizeof(args));
 
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                if (encoder->crtc == crtc) {
                        radeon_encoder = to_radeon_encoder(encoder);
+                       encoder_mode = atombios_get_encoder_mode(encoder);
                        break;
                }
        }
@@ -542,26 +647,24 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
        if (!radeon_encoder)
                return;
 
-       if (radeon_crtc->crtc_id == 0)
+       switch (radeon_crtc->pll_id) {
+       case ATOM_PPLL1:
                pll = &rdev->clock.p1pll;
-       else
+               break;
+       case ATOM_PPLL2:
                pll = &rdev->clock.p2pll;
+               break;
+       case ATOM_DCPLL:
+       case ATOM_PPLL_INVALID:
+               pll = &rdev->clock.dcpll;
+               break;
+       }
 
        /* adjust pixel clock as needed */
        adjusted_clock = atombios_adjust_pll(crtc, mode, pll);
 
-       if (ASIC_IS_AVIVO(rdev)) {
-               if (radeon_new_pll)
-                       radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock,
-                                                &fb_div, &frac_fb_div,
-                                                &ref_div, &post_div);
-               else
-                       radeon_compute_pll(pll, adjusted_clock, &pll_clock,
-                                          &fb_div, &frac_fb_div,
-                                          &ref_div, &post_div);
-       } else
-               radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
-                                  &ref_div, &post_div);
+       radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
+                          &ref_div, &post_div);
 
        index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
        atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
@@ -576,8 +679,7 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
                        args.v1.usFbDiv = cpu_to_le16(fb_div);
                        args.v1.ucFracFbDiv = frac_fb_div;
                        args.v1.ucPostDiv = post_div;
-                       args.v1.ucPpll =
-                           radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
+                       args.v1.ucPpll = radeon_crtc->pll_id;
                        args.v1.ucCRTC = radeon_crtc->crtc_id;
                        args.v1.ucRefDivSrc = 1;
                        break;
@@ -587,8 +689,7 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
                        args.v2.usFbDiv = cpu_to_le16(fb_div);
                        args.v2.ucFracFbDiv = frac_fb_div;
                        args.v2.ucPostDiv = post_div;
-                       args.v2.ucPpll =
-                           radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
+                       args.v2.ucPpll = radeon_crtc->pll_id;
                        args.v2.ucCRTC = radeon_crtc->crtc_id;
                        args.v2.ucRefDivSrc = 1;
                        break;
@@ -598,12 +699,22 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
                        args.v3.usFbDiv = cpu_to_le16(fb_div);
                        args.v3.ucFracFbDiv = frac_fb_div;
                        args.v3.ucPostDiv = post_div;
-                       args.v3.ucPpll =
-                           radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
-                       args.v3.ucMiscInfo = (radeon_crtc->crtc_id << 2);
+                       args.v3.ucPpll = radeon_crtc->pll_id;
+                       args.v3.ucMiscInfo = (radeon_crtc->pll_id << 2);
                        args.v3.ucTransmitterId = radeon_encoder->encoder_id;
-                       args.v3.ucEncoderMode =
-                           atombios_get_encoder_mode(encoder);
+                       args.v3.ucEncoderMode = encoder_mode;
+                       break;
+               case 5:
+                       args.v5.ucCRTC = radeon_crtc->crtc_id;
+                       args.v5.usPixelClock = cpu_to_le16(mode->clock / 10);
+                       args.v5.ucRefDiv = ref_div;
+                       args.v5.usFbDiv = cpu_to_le16(fb_div);
+                       args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
+                       args.v5.ucPostDiv = post_div;
+                       args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
+                       args.v5.ucTransmitterID = radeon_encoder->encoder_id;
+                       args.v5.ucEncoderMode = encoder_mode;
+                       args.v5.ucPpll = radeon_crtc->pll_id;
                        break;
                default:
                        DRM_ERROR("Unknown table version %d %d\n", frev, crev);
@@ -618,6 +729,140 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+static int evergreen_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+                                  struct drm_framebuffer *old_fb)
+{
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_framebuffer *radeon_fb;
+       struct drm_gem_object *obj;
+       struct radeon_bo *rbo;
+       uint64_t fb_location;
+       uint32_t fb_format, fb_pitch_pixels, tiling_flags;
+       int r;
+
+       /* no fb bound */
+       if (!crtc->fb) {
+               DRM_DEBUG("No FB bound\n");
+               return 0;
+       }
+
+       radeon_fb = to_radeon_framebuffer(crtc->fb);
+
+       /* Pin framebuffer & get tilling informations */
+       obj = radeon_fb->obj;
+       rbo = obj->driver_private;
+       r = radeon_bo_reserve(rbo, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
+       if (unlikely(r != 0)) {
+               radeon_bo_unreserve(rbo);
+               return -EINVAL;
+       }
+       radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
+       radeon_bo_unreserve(rbo);
+
+       switch (crtc->fb->bits_per_pixel) {
+       case 8:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED));
+               break;
+       case 15:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555));
+               break;
+       case 16:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565));
+               break;
+       case 24:
+       case 32:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888));
+               break;
+       default:
+               DRM_ERROR("Unsupported screen depth %d\n",
+                         crtc->fb->bits_per_pixel);
+               return -EINVAL;
+       }
+
+       switch (radeon_crtc->crtc_id) {
+       case 0:
+               WREG32(AVIVO_D1VGA_CONTROL, 0);
+               break;
+       case 1:
+               WREG32(AVIVO_D2VGA_CONTROL, 0);
+               break;
+       case 2:
+               WREG32(EVERGREEN_D3VGA_CONTROL, 0);
+               break;
+       case 3:
+               WREG32(EVERGREEN_D4VGA_CONTROL, 0);
+               break;
+       case 4:
+               WREG32(EVERGREEN_D5VGA_CONTROL, 0);
+               break;
+       case 5:
+               WREG32(EVERGREEN_D6VGA_CONTROL, 0);
+               break;
+       default:
+               break;
+       }
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
+              upper_32_bits(fb_location));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
+              upper_32_bits(fb_location));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
+              (u32)fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
+              (u32) fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK);
+       WREG32(EVERGREEN_GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format);
+
+       WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_X_START + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_Y_START + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_X_END + radeon_crtc->crtc_offset, crtc->fb->width);
+       WREG32(EVERGREEN_GRPH_Y_END + radeon_crtc->crtc_offset, crtc->fb->height);
+
+       fb_pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8);
+       WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels);
+       WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
+
+       WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset,
+              crtc->mode.vdisplay);
+       x &= ~3;
+       y &= ~1;
+       WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset,
+              (x << 16) | y);
+       WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset,
+              (crtc->mode.hdisplay << 16) | crtc->mode.vdisplay);
+
+       if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE)
+               WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset,
+                      EVERGREEN_INTERLEAVE_EN);
+       else
+               WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, 0);
+
+       if (old_fb && old_fb != crtc->fb) {
+               radeon_fb = to_radeon_framebuffer(old_fb);
+               rbo = radeon_fb->obj->driver_private;
+               r = radeon_bo_reserve(rbo, false);
+               if (unlikely(r != 0))
+                       return r;
+               radeon_bo_unpin(rbo);
+               radeon_bo_unreserve(rbo);
+       }
+
+       /* Bytes per pixel may have changed */
+       radeon_bandwidth_update(rdev);
+
+       return 0;
+}
+
 static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
                               struct drm_framebuffer *old_fb)
 {
@@ -755,7 +1000,9 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
        struct drm_device *dev = crtc->dev;
        struct radeon_device *rdev = dev->dev_private;
 
-       if (ASIC_IS_AVIVO(rdev))
+       if (ASIC_IS_DCE4(rdev))
+               return evergreen_crtc_set_base(crtc, x, y, old_fb);
+       else if (ASIC_IS_AVIVO(rdev))
                return avivo_crtc_set_base(crtc, x, y, old_fb);
        else
                return radeon_crtc_set_base(crtc, x, y, old_fb);
@@ -785,6 +1032,46 @@ static void radeon_legacy_atom_fixup(struct drm_crtc *crtc)
        }
 }
 
+static int radeon_atom_pick_pll(struct drm_crtc *crtc)
+{
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct drm_encoder *test_encoder;
+       struct drm_crtc *test_crtc;
+       uint32_t pll_in_use = 0;
+
+       if (ASIC_IS_DCE4(rdev)) {
+               /* if crtc is driving DP and we have an ext clock, use that */
+               list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
+                       if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
+                               if (atombios_get_encoder_mode(test_encoder) == ATOM_ENCODER_MODE_DP) {
+                                       if (rdev->clock.dp_extclk)
+                                               return ATOM_PPLL_INVALID;
+                               }
+                       }
+               }
+
+               /* otherwise, pick one of the plls */
+               list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+                       struct radeon_crtc *radeon_test_crtc;
+
+                       if (crtc == test_crtc)
+                               continue;
+
+                       radeon_test_crtc = to_radeon_crtc(test_crtc);
+                       if ((radeon_test_crtc->pll_id >= ATOM_PPLL1) &&
+                           (radeon_test_crtc->pll_id <= ATOM_PPLL2))
+                               pll_in_use |= (1 << radeon_test_crtc->pll_id);
+               }
+               if (!(pll_in_use & 1))
+                       return ATOM_PPLL1;
+               return ATOM_PPLL2;
+       } else
+               return radeon_crtc->crtc_id;
+
+}
+
 int atombios_crtc_mode_set(struct drm_crtc *crtc,
                           struct drm_display_mode *mode,
                           struct drm_display_mode *adjusted_mode,
@@ -796,19 +1083,27 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
 
        /* TODO color tiling */
 
+       /* pick pll */
+       radeon_crtc->pll_id = radeon_atom_pick_pll(crtc);
+
        atombios_set_ss(crtc, 0);
+       /* always set DCPLL */
+       if (ASIC_IS_DCE4(rdev))
+               atombios_crtc_set_dcpll(crtc);
        atombios_crtc_set_pll(crtc, adjusted_mode);
        atombios_set_ss(crtc, 1);
-       atombios_crtc_set_timing(crtc, adjusted_mode);
 
-       if (ASIC_IS_AVIVO(rdev))
-               atombios_crtc_set_base(crtc, x, y, old_fb);
+       if (ASIC_IS_DCE4(rdev))
+               atombios_set_crtc_dtd_timing(crtc, adjusted_mode);
+       else if (ASIC_IS_AVIVO(rdev))
+               atombios_crtc_set_timing(crtc, adjusted_mode);
        else {
+               atombios_crtc_set_timing(crtc, adjusted_mode);
                if (radeon_crtc->crtc_id == 0)
                        atombios_set_crtc_dtd_timing(crtc, adjusted_mode);
-               atombios_crtc_set_base(crtc, x, y, old_fb);
                radeon_legacy_atom_fixup(crtc);
        }
+       atombios_crtc_set_base(crtc, x, y, old_fb);
        atombios_overscan_setup(crtc, mode, adjusted_mode);
        atombios_scaler_setup(crtc);
        return 0;
@@ -825,14 +1120,14 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
 
 static void atombios_crtc_prepare(struct drm_crtc *crtc)
 {
-       atombios_lock_crtc(crtc, 1);
+       atombios_lock_crtc(crtc, ATOM_ENABLE);
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
 }
 
 static void atombios_crtc_commit(struct drm_crtc *crtc)
 {
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-       atombios_lock_crtc(crtc, 0);
+       atombios_lock_crtc(crtc, ATOM_DISABLE);
 }
 
 static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
@@ -848,8 +1143,37 @@ static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
 void radeon_atombios_init_crtc(struct drm_device *dev,
                               struct radeon_crtc *radeon_crtc)
 {
-       if (radeon_crtc->crtc_id == 1)
-               radeon_crtc->crtc_offset =
-                   AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL;
+       struct radeon_device *rdev = dev->dev_private;
+
+       if (ASIC_IS_DCE4(rdev)) {
+               switch (radeon_crtc->crtc_id) {
+               case 0:
+               default:
+                       radeon_crtc->crtc_offset = EVERGREEN_CRTC0_REGISTER_OFFSET;
+                       break;
+               case 1:
+                       radeon_crtc->crtc_offset = EVERGREEN_CRTC1_REGISTER_OFFSET;
+                       break;
+               case 2:
+                       radeon_crtc->crtc_offset = EVERGREEN_CRTC2_REGISTER_OFFSET;
+                       break;
+               case 3:
+                       radeon_crtc->crtc_offset = EVERGREEN_CRTC3_REGISTER_OFFSET;
+                       break;
+               case 4:
+                       radeon_crtc->crtc_offset = EVERGREEN_CRTC4_REGISTER_OFFSET;
+                       break;
+               case 5:
+                       radeon_crtc->crtc_offset = EVERGREEN_CRTC5_REGISTER_OFFSET;
+                       break;
+               }
+       } else {
+               if (radeon_crtc->crtc_id == 1)
+                       radeon_crtc->crtc_offset =
+                               AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL;
+               else
+                       radeon_crtc->crtc_offset = 0;
+       }
+       radeon_crtc->pll_id = -1;
        drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs);
 }
index 99915a682d593deb4b4aabaf2cd81fd902bc9103..8a133bda00a25b092e764f1eec2bca80c57cb569 100644 (file)
@@ -321,6 +321,10 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
                train_set[lane] = v | p;
 }
 
+union aux_channel_transaction {
+       PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
+       PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
+};
 
 /* radeon aux chan functions */
 bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
@@ -329,7 +333,7 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
 {
        struct drm_device *dev = chan->dev;
        struct radeon_device *rdev = dev->dev_private;
-       PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION args;
+       union aux_channel_transaction args;
        int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
        unsigned char *base;
        int retry_count = 0;
@@ -341,31 +345,33 @@ bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
 retry:
        memcpy(base, req_bytes, num_bytes);
 
-       args.lpAuxRequest = 0;
-       args.lpDataOut = 16;
-       args.ucDataOutLen = 0;
-       args.ucChannelID = chan->rec.i2c_id;
-       args.ucDelay = delay / 10;
+       args.v1.lpAuxRequest = 0;
+       args.v1.lpDataOut = 16;
+       args.v1.ucDataOutLen = 0;
+       args.v1.ucChannelID = chan->rec.i2c_id;
+       args.v1.ucDelay = delay / 10;
+       if (ASIC_IS_DCE4(rdev))
+               args.v2.ucHPD_ID = chan->rec.hpd_id;
 
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
-       if (args.ucReplyStatus && !args.ucDataOutLen) {
-               if (args.ucReplyStatus == 0x20 && retry_count++ < 10)
+       if (args.v1.ucReplyStatus && !args.v1.ucDataOutLen) {
+               if (args.v1.ucReplyStatus == 0x20 && retry_count++ < 10)
                        goto retry;
                DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n",
                          req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
-                         chan->rec.i2c_id, args.ucReplyStatus, retry_count);
+                         chan->rec.i2c_id, args.v1.ucReplyStatus, retry_count);
                return false;
        }
 
-       if (args.ucDataOutLen && read_byte && read_buf_len) {
-               if (read_buf_len < args.ucDataOutLen) {
+       if (args.v1.ucDataOutLen && read_byte && read_buf_len) {
+               if (read_buf_len < args.v1.ucDataOutLen) {
                        DRM_ERROR("Buffer to small for return answer %d %d\n",
-                                 read_buf_len, args.ucDataOutLen);
+                                 read_buf_len, args.v1.ucDataOutLen);
                        return false;
                }
                {
-                       int len = min(read_buf_len, args.ucDataOutLen);
+                       int len = min(read_buf_len, args.v1.ucDataOutLen);
                        memcpy(read_byte, base + 16, len);
                }
        }
@@ -626,12 +632,19 @@ void dp_link_train(struct drm_encoder *encoder,
        dp_set_link_bw_lanes(radeon_connector, link_configuration);
        /* disable downspread on the sink */
        dp_set_downspread(radeon_connector, 0);
-       /* start training on the source */
-       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
-                                 dig_connector->dp_clock, enc_id, 0);
-       /* set training pattern 1 on the source */
-       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
-                                 dig_connector->dp_clock, enc_id, 0);
+       if (ASIC_IS_DCE4(rdev)) {
+               /* start training on the source */
+               atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START);
+               /* set training pattern 1 on the source */
+               atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1);
+       } else {
+               /* start training on the source */
+               radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
+                                         dig_connector->dp_clock, enc_id, 0);
+               /* set training pattern 1 on the source */
+               radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
+                                         dig_connector->dp_clock, enc_id, 0);
+       }
 
        /* set initial vs/emph */
        memset(train_set, 0, 4);
@@ -691,8 +704,11 @@ void dp_link_train(struct drm_encoder *encoder,
        /* set training pattern 2 on the sink */
        dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
        /* set training pattern 2 on the source */
-       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
-                                 dig_connector->dp_clock, enc_id, 1);
+       if (ASIC_IS_DCE4(rdev))
+               atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2);
+       else
+               radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
+                                         dig_connector->dp_clock, enc_id, 1);
 
        /* channel equalization loop */
        tries = 0;
@@ -729,7 +745,11 @@ void dp_link_train(struct drm_encoder *encoder,
                          >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
 
        /* disable the training pattern on the sink */
-       dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
+       if (ASIC_IS_DCE4(rdev))
+               atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE);
+       else
+               radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
+                                         dig_connector->dp_clock, enc_id, 0);
 
        radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
                                  dig_connector->dp_clock, enc_id, 0);
index d4e6e6e4a93888a14a738dd3484279d4f5e28d9c..3c391e7e9fd41ef2834a668b73afcf8d119a9dff 100644 (file)
 
 #define        D1CRTC_CONTROL                                  0x6080
 #define                CRTC_EN                                         (1 << 0)
+#define        D1CRTC_STATUS                                   0x609c
 #define        D1CRTC_UPDATE_LOCK                              0x60E8
 #define        D1GRPH_PRIMARY_SURFACE_ADDRESS                  0x6110
 #define        D1GRPH_SECONDARY_SURFACE_ADDRESS                0x6118
 
 #define        D2CRTC_CONTROL                                  0x6880
+#define        D2CRTC_STATUS                                   0x689c
 #define        D2CRTC_UPDATE_LOCK                              0x68E8
 #define        D2GRPH_PRIMARY_SURFACE_ADDRESS                  0x6910
 #define        D2GRPH_SECONDARY_SURFACE_ADDRESS                0x6918
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
new file mode 100644 (file)
index 0000000..bd2e7aa
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+ * Copyright 2010 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Alex Deucher
+ */
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "radeon_drm.h"
+#include "rv770d.h"
+#include "atom.h"
+#include "avivod.h"
+#include "evergreen_reg.h"
+
+static void evergreen_gpu_init(struct radeon_device *rdev);
+void evergreen_fini(struct radeon_device *rdev);
+
+bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
+{
+       bool connected = false;
+       /* XXX */
+       return connected;
+}
+
+void evergreen_hpd_set_polarity(struct radeon_device *rdev,
+                               enum radeon_hpd_id hpd)
+{
+       /* XXX */
+}
+
+void evergreen_hpd_init(struct radeon_device *rdev)
+{
+       /* XXX */
+}
+
+
+void evergreen_bandwidth_update(struct radeon_device *rdev)
+{
+       /* XXX */
+}
+
+void evergreen_hpd_fini(struct radeon_device *rdev)
+{
+       /* XXX */
+}
+
+static int evergreen_mc_wait_for_idle(struct radeon_device *rdev)
+{
+       unsigned i;
+       u32 tmp;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               /* read MC_STATUS */
+               tmp = RREG32(SRBM_STATUS) & 0x1F00;
+               if (!tmp)
+                       return 0;
+               udelay(1);
+       }
+       return -1;
+}
+
+/*
+ * GART
+ */
+int evergreen_pcie_gart_enable(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int r, i;
+
+       if (rdev->gart.table.vram.robj == NULL) {
+               dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
+               return -EINVAL;
+       }
+       r = radeon_gart_table_vram_pin(rdev);
+       if (r)
+               return r;
+       radeon_gart_restore(rdev);
+       /* Setup L2 cache */
+       WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
+                               ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
+                               EFFECTIVE_L2_QUEUE_SIZE(7));
+       WREG32(VM_L2_CNTL2, 0);
+       WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2));
+       /* Setup TLB control */
+       tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING |
+               SYSTEM_ACCESS_MODE_NOT_IN_SYS |
+               SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU |
+               EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5);
+       WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
+       WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
+       WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12);
+       WREG32(VM_CONTEXT0_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) |
+                               RANGE_PROTECTION_FAULT_ENABLE_DEFAULT);
+       WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
+                       (u32)(rdev->dummy_page.addr >> 12));
+       for (i = 1; i < 7; i++)
+               WREG32(VM_CONTEXT0_CNTL + (i * 4), 0);
+
+       r600_pcie_gart_tlb_flush(rdev);
+       rdev->gart.ready = true;
+       return 0;
+}
+
+void evergreen_pcie_gart_disable(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i, r;
+
+       /* Disable all tables */
+       for (i = 0; i < 7; i++)
+               WREG32(VM_CONTEXT0_CNTL + (i * 4), 0);
+
+       /* Setup L2 cache */
+       WREG32(VM_L2_CNTL, ENABLE_L2_FRAGMENT_PROCESSING |
+                               EFFECTIVE_L2_QUEUE_SIZE(7));
+       WREG32(VM_L2_CNTL2, 0);
+       WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2));
+       /* Setup TLB control */
+       tmp = EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5);
+       WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
+       WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
+       WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
+       if (rdev->gart.table.vram.robj) {
+               r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->gart.table.vram.robj);
+                       radeon_bo_unpin(rdev->gart.table.vram.robj);
+                       radeon_bo_unreserve(rdev->gart.table.vram.robj);
+               }
+       }
+}
+
+void evergreen_pcie_gart_fini(struct radeon_device *rdev)
+{
+       evergreen_pcie_gart_disable(rdev);
+       radeon_gart_table_vram_free(rdev);
+       radeon_gart_fini(rdev);
+}
+
+
+void evergreen_agp_enable(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i;
+
+       /* Setup L2 cache */
+       WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
+                               ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
+                               EFFECTIVE_L2_QUEUE_SIZE(7));
+       WREG32(VM_L2_CNTL2, 0);
+       WREG32(VM_L2_CNTL3, BANK_SELECT(0) | CACHE_UPDATE_MODE(2));
+       /* Setup TLB control */
+       tmp = ENABLE_L1_TLB | ENABLE_L1_FRAGMENT_PROCESSING |
+               SYSTEM_ACCESS_MODE_NOT_IN_SYS |
+               SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU |
+               EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5);
+       WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
+       WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
+       WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
+       WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
+       for (i = 0; i < 7; i++)
+               WREG32(VM_CONTEXT0_CNTL + (i * 4), 0);
+}
+
+static void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
+{
+       save->vga_control[0] = RREG32(D1VGA_CONTROL);
+       save->vga_control[1] = RREG32(D2VGA_CONTROL);
+       save->vga_control[2] = RREG32(EVERGREEN_D3VGA_CONTROL);
+       save->vga_control[3] = RREG32(EVERGREEN_D4VGA_CONTROL);
+       save->vga_control[4] = RREG32(EVERGREEN_D5VGA_CONTROL);
+       save->vga_control[5] = RREG32(EVERGREEN_D6VGA_CONTROL);
+       save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
+       save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
+       save->crtc_control[0] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET);
+       save->crtc_control[1] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET);
+       save->crtc_control[2] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET);
+       save->crtc_control[3] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET);
+       save->crtc_control[4] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET);
+       save->crtc_control[5] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
+
+       /* Stop all video */
+       WREG32(VGA_RENDER_CONTROL, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+
+       WREG32(D1VGA_CONTROL, 0);
+       WREG32(D2VGA_CONTROL, 0);
+       WREG32(EVERGREEN_D3VGA_CONTROL, 0);
+       WREG32(EVERGREEN_D4VGA_CONTROL, 0);
+       WREG32(EVERGREEN_D5VGA_CONTROL, 0);
+       WREG32(EVERGREEN_D6VGA_CONTROL, 0);
+}
+
+static void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save)
+{
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC0_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC0_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC0_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC0_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC1_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC1_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC1_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC1_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC2_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC2_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC2_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC2_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC3_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC3_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC3_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC3_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC4_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC4_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC4_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC4_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC5_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + EVERGREEN_CRTC5_REGISTER_OFFSET,
+              upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + EVERGREEN_CRTC5_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + EVERGREEN_CRTC5_REGISTER_OFFSET,
+              (u32)rdev->mc.vram_start);
+
+       WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH, upper_32_bits(rdev->mc.vram_start));
+       WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS, (u32)rdev->mc.vram_start);
+       /* Unlock host access */
+       WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
+       mdelay(1);
+       /* Restore video state */
+       WREG32(D1VGA_CONTROL, save->vga_control[0]);
+       WREG32(D2VGA_CONTROL, save->vga_control[1]);
+       WREG32(EVERGREEN_D3VGA_CONTROL, save->vga_control[2]);
+       WREG32(EVERGREEN_D4VGA_CONTROL, save->vga_control[3]);
+       WREG32(EVERGREEN_D5VGA_CONTROL, save->vga_control[4]);
+       WREG32(EVERGREEN_D6VGA_CONTROL, save->vga_control[5]);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, save->crtc_control[0]);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, save->crtc_control[1]);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, save->crtc_control[2]);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, save->crtc_control[3]);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, save->crtc_control[4]);
+       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, save->crtc_control[5]);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
+       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+       WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
+}
+
+static void evergreen_mc_program(struct radeon_device *rdev)
+{
+       struct evergreen_mc_save save;
+       u32 tmp;
+       int i, j;
+
+       /* Initialize HDP */
+       for (i = 0, j = 0; i < 32; i++, j += 0x18) {
+               WREG32((0x2c14 + j), 0x00000000);
+               WREG32((0x2c18 + j), 0x00000000);
+               WREG32((0x2c1c + j), 0x00000000);
+               WREG32((0x2c20 + j), 0x00000000);
+               WREG32((0x2c24 + j), 0x00000000);
+       }
+       WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0);
+
+       evergreen_mc_stop(rdev, &save);
+       if (evergreen_mc_wait_for_idle(rdev)) {
+               dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+       }
+       /* Lockout access through VGA aperture*/
+       WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE);
+       /* Update configuration */
+       if (rdev->flags & RADEON_IS_AGP) {
+               if (rdev->mc.vram_start < rdev->mc.gtt_start) {
+                       /* VRAM before AGP */
+                       WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
+                               rdev->mc.vram_start >> 12);
+                       WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
+                               rdev->mc.gtt_end >> 12);
+               } else {
+                       /* VRAM after AGP */
+                       WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
+                               rdev->mc.gtt_start >> 12);
+                       WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
+                               rdev->mc.vram_end >> 12);
+               }
+       } else {
+               WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
+                       rdev->mc.vram_start >> 12);
+               WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
+                       rdev->mc.vram_end >> 12);
+       }
+       WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, 0);
+       tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16;
+       tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF);
+       WREG32(MC_VM_FB_LOCATION, tmp);
+       WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8));
+       WREG32(HDP_NONSURFACE_INFO, (2 << 7));
+       WREG32(HDP_NONSURFACE_SIZE, (rdev->mc.mc_vram_size - 1) | 0x3FF);
+       if (rdev->flags & RADEON_IS_AGP) {
+               WREG32(MC_VM_AGP_TOP, rdev->mc.gtt_end >> 16);
+               WREG32(MC_VM_AGP_BOT, rdev->mc.gtt_start >> 16);
+               WREG32(MC_VM_AGP_BASE, rdev->mc.agp_base >> 22);
+       } else {
+               WREG32(MC_VM_AGP_BASE, 0);
+               WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF);
+               WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF);
+       }
+       if (evergreen_mc_wait_for_idle(rdev)) {
+               dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+       }
+       evergreen_mc_resume(rdev, &save);
+       /* we need to own VRAM, so turn off the VGA renderer here
+        * to stop it overwriting our objects */
+       rv515_vga_render_disable(rdev);
+}
+
+#if 0
+/*
+ * CP.
+ */
+static void evergreen_cp_stop(struct radeon_device *rdev)
+{
+       /* XXX */
+}
+
+
+static int evergreen_cp_load_microcode(struct radeon_device *rdev)
+{
+       /* XXX */
+
+       return 0;
+}
+
+
+/*
+ * Core functions
+ */
+static u32 evergreen_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
+                                                 u32 num_backends,
+                                                 u32 backend_disable_mask)
+{
+       u32 backend_map = 0;
+
+       return backend_map;
+}
+#endif
+
+static void evergreen_gpu_init(struct radeon_device *rdev)
+{
+       /* XXX */
+}
+
+int evergreen_mc_init(struct radeon_device *rdev)
+{
+       fixed20_12 a;
+       u32 tmp;
+       int chansize, numchan;
+
+       /* Get VRAM informations */
+       rdev->mc.vram_is_ddr = true;
+       tmp = RREG32(MC_ARB_RAMCFG);
+       if (tmp & CHANSIZE_OVERRIDE) {
+               chansize = 16;
+       } else if (tmp & CHANSIZE_MASK) {
+               chansize = 64;
+       } else {
+               chansize = 32;
+       }
+       tmp = RREG32(MC_SHARED_CHMAP);
+       switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
+       case 0:
+       default:
+               numchan = 1;
+               break;
+       case 1:
+               numchan = 2;
+               break;
+       case 2:
+               numchan = 4;
+               break;
+       case 3:
+               numchan = 8;
+               break;
+       }
+       rdev->mc.vram_width = numchan * chansize;
+       /* Could aper size report 0 ? */
+       rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
+       rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
+       /* Setup GPU memory space */
+       /* size in MB on evergreen */
+       rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+       rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+       rdev->mc.visible_vram_size = rdev->mc.aper_size;
+       /* FIXME remove this once we support unmappable VRAM */
+       if (rdev->mc.mc_vram_size > rdev->mc.aper_size) {
+               rdev->mc.mc_vram_size = rdev->mc.aper_size;
+               rdev->mc.real_vram_size = rdev->mc.aper_size;
+       }
+       r600_vram_gtt_location(rdev, &rdev->mc);
+       /* FIXME: we should enforce default clock in case GPU is not in
+        * default setup
+        */
+       a.full = rfixed_const(100);
+       rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk);
+       rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a);
+       return 0;
+}
+
+int evergreen_gpu_reset(struct radeon_device *rdev)
+{
+       /* FIXME: implement for evergreen */
+       return 0;
+}
+
+static int evergreen_startup(struct radeon_device *rdev)
+{
+#if 0
+       int r;
+
+       if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
+               r = r600_init_microcode(rdev);
+               if (r) {
+                       DRM_ERROR("Failed to load firmware!\n");
+                       return r;
+               }
+       }
+#endif
+       evergreen_mc_program(rdev);
+#if 0
+       if (rdev->flags & RADEON_IS_AGP) {
+               evergreem_agp_enable(rdev);
+       } else {
+               r = evergreen_pcie_gart_enable(rdev);
+               if (r)
+                       return r;
+       }
+#endif
+       evergreen_gpu_init(rdev);
+#if 0
+       if (!rdev->r600_blit.shader_obj) {
+               r = r600_blit_init(rdev);
+               if (r) {
+                       DRM_ERROR("radeon: failed blitter (%d).\n", r);
+                       return r;
+               }
+       }
+
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+                       &rdev->r600_blit.shader_gpu_addr);
+       radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+       if (r) {
+               DRM_ERROR("failed to pin blit object %d\n", r);
+               return r;
+       }
+
+       /* Enable IRQ */
+       r = r600_irq_init(rdev);
+       if (r) {
+               DRM_ERROR("radeon: IH init failed (%d).\n", r);
+               radeon_irq_kms_fini(rdev);
+               return r;
+       }
+       r600_irq_set(rdev);
+
+       r = radeon_ring_init(rdev, rdev->cp.ring_size);
+       if (r)
+               return r;
+       r = evergreen_cp_load_microcode(rdev);
+       if (r)
+               return r;
+       r = r600_cp_resume(rdev);
+       if (r)
+               return r;
+       /* write back buffer are not vital so don't worry about failure */
+       r600_wb_enable(rdev);
+#endif
+       return 0;
+}
+
+int evergreen_resume(struct radeon_device *rdev)
+{
+       int r;
+
+       /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw,
+        * posting will perform necessary task to bring back GPU into good
+        * shape.
+        */
+       /* post card */
+       atom_asic_init(rdev->mode_info.atom_context);
+       /* Initialize clocks */
+       r = radeon_clocks_init(rdev);
+       if (r) {
+               return r;
+       }
+
+       r = evergreen_startup(rdev);
+       if (r) {
+               DRM_ERROR("r600 startup failed on resume\n");
+               return r;
+       }
+#if 0
+       r = r600_ib_test(rdev);
+       if (r) {
+               DRM_ERROR("radeon: failled testing IB (%d).\n", r);
+               return r;
+       }
+#endif
+       return r;
+
+}
+
+int evergreen_suspend(struct radeon_device *rdev)
+{
+#if 0
+       int r;
+
+       /* FIXME: we should wait for ring to be empty */
+       r700_cp_stop(rdev);
+       rdev->cp.ready = false;
+       r600_wb_disable(rdev);
+       evergreen_pcie_gart_disable(rdev);
+       /* unpin shaders bo */
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (likely(r == 0)) {
+               radeon_bo_unpin(rdev->r600_blit.shader_obj);
+               radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+       }
+#endif
+       return 0;
+}
+
+static bool evergreen_card_posted(struct radeon_device *rdev)
+{
+       u32 reg;
+
+       /* first check CRTCs */
+       reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) |
+               RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) |
+               RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET) |
+               RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET) |
+               RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET) |
+               RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
+       if (reg & EVERGREEN_CRTC_MASTER_EN)
+               return true;
+
+       /* then check MEM_SIZE, in case the crtcs are off */
+       if (RREG32(CONFIG_MEMSIZE))
+               return true;
+
+       return false;
+}
+
+/* Plan is to move initialization in that function and use
+ * helper function so that radeon_device_init pretty much
+ * do nothing more than calling asic specific function. This
+ * should also allow to remove a bunch of callback function
+ * like vram_info.
+ */
+int evergreen_init(struct radeon_device *rdev)
+{
+       int r;
+
+       r = radeon_dummy_page_init(rdev);
+       if (r)
+               return r;
+       /* This don't do much */
+       r = radeon_gem_init(rdev);
+       if (r)
+               return r;
+       /* Read BIOS */
+       if (!radeon_get_bios(rdev)) {
+               if (ASIC_IS_AVIVO(rdev))
+                       return -EINVAL;
+       }
+       /* Must be an ATOMBIOS */
+       if (!rdev->is_atom_bios) {
+               dev_err(rdev->dev, "Expecting atombios for R600 GPU\n");
+               return -EINVAL;
+       }
+       r = radeon_atombios_init(rdev);
+       if (r)
+               return r;
+       /* Post card if necessary */
+       if (!evergreen_card_posted(rdev)) {
+               if (!rdev->bios) {
+                       dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
+                       return -EINVAL;
+               }
+               DRM_INFO("GPU not posted. posting now...\n");
+               atom_asic_init(rdev->mode_info.atom_context);
+       }
+       /* Initialize scratch registers */
+       r600_scratch_init(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
+       /* Initialize clocks */
+       radeon_get_clock_info(rdev->ddev);
+       r = radeon_clocks_init(rdev);
+       if (r)
+               return r;
+       /* Initialize power management */
+       radeon_pm_init(rdev);
+       /* Fence driver */
+       r = radeon_fence_driver_init(rdev);
+       if (r)
+               return r;
+       /* initialize AGP */
+       if (rdev->flags & RADEON_IS_AGP) {
+               r = radeon_agp_init(rdev);
+               if (r)
+                       radeon_agp_disable(rdev);
+       }
+       /* initialize memory controller */
+       r = evergreen_mc_init(rdev);
+       if (r)
+               return r;
+       /* Memory manager */
+       r = radeon_bo_init(rdev);
+       if (r)
+               return r;
+#if 0
+       r = radeon_irq_kms_init(rdev);
+       if (r)
+               return r;
+
+       rdev->cp.ring_obj = NULL;
+       r600_ring_init(rdev, 1024 * 1024);
+
+       rdev->ih.ring_obj = NULL;
+       r600_ih_ring_init(rdev, 64 * 1024);
+
+       r = r600_pcie_gart_init(rdev);
+       if (r)
+               return r;
+#endif
+       rdev->accel_working = false;
+       r = evergreen_startup(rdev);
+       if (r) {
+               evergreen_suspend(rdev);
+               /*r600_wb_fini(rdev);*/
+               /*radeon_ring_fini(rdev);*/
+               /*evergreen_pcie_gart_fini(rdev);*/
+               rdev->accel_working = false;
+       }
+       if (rdev->accel_working) {
+               r = radeon_ib_pool_init(rdev);
+               if (r) {
+                       DRM_ERROR("radeon: failed initializing IB pool (%d).\n", r);
+                       rdev->accel_working = false;
+               }
+               r = r600_ib_test(rdev);
+               if (r) {
+                       DRM_ERROR("radeon: failed testing IB (%d).\n", r);
+                       rdev->accel_working = false;
+               }
+       }
+       return 0;
+}
+
+void evergreen_fini(struct radeon_device *rdev)
+{
+       evergreen_suspend(rdev);
+#if 0
+       r600_blit_fini(rdev);
+       r600_irq_fini(rdev);
+       radeon_irq_kms_fini(rdev);
+       radeon_ring_fini(rdev);
+       r600_wb_fini(rdev);
+       evergreen_pcie_gart_fini(rdev);
+#endif
+       radeon_gem_fini(rdev);
+       radeon_fence_driver_fini(rdev);
+       radeon_clocks_fini(rdev);
+       radeon_agp_fini(rdev);
+       radeon_bo_fini(rdev);
+       radeon_atombios_fini(rdev);
+       kfree(rdev->bios);
+       rdev->bios = NULL;
+       radeon_dummy_page_fini(rdev);
+}
diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h
new file mode 100644 (file)
index 0000000..f7c7c96
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2010 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Alex Deucher
+ */
+#ifndef __EVERGREEN_REG_H__
+#define __EVERGREEN_REG_H__
+
+/* evergreen */
+#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS               0x310
+#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH          0x324
+#define EVERGREEN_D3VGA_CONTROL                         0x3e0
+#define EVERGREEN_D4VGA_CONTROL                         0x3e4
+#define EVERGREEN_D5VGA_CONTROL                         0x3e8
+#define EVERGREEN_D6VGA_CONTROL                         0x3ec
+
+#define EVERGREEN_P1PLL_SS_CNTL                         0x414
+#define EVERGREEN_P2PLL_SS_CNTL                         0x454
+#       define EVERGREEN_PxPLL_SS_EN                    (1 << 12)
+/* GRPH blocks at 0x6800, 0x7400, 0x10000, 0x10c00, 0x11800, 0x12400 */
+#define EVERGREEN_GRPH_ENABLE                           0x6800
+#define EVERGREEN_GRPH_CONTROL                          0x6804
+#       define EVERGREEN_GRPH_DEPTH(x)                  (((x) & 0x3) << 0)
+#       define EVERGREEN_GRPH_DEPTH_8BPP                0
+#       define EVERGREEN_GRPH_DEPTH_16BPP               1
+#       define EVERGREEN_GRPH_DEPTH_32BPP               2
+#       define EVERGREEN_GRPH_FORMAT(x)                 (((x) & 0x7) << 8)
+/* 8 BPP */
+#       define EVERGREEN_GRPH_FORMAT_INDEXED            0
+/* 16 BPP */
+#       define EVERGREEN_GRPH_FORMAT_ARGB1555           0
+#       define EVERGREEN_GRPH_FORMAT_ARGB565            1
+#       define EVERGREEN_GRPH_FORMAT_ARGB4444           2
+#       define EVERGREEN_GRPH_FORMAT_AI88               3
+#       define EVERGREEN_GRPH_FORMAT_MONO16             4
+#       define EVERGREEN_GRPH_FORMAT_BGRA5551           5
+/* 32 BPP */
+#       define EVERGREEN_GRPH_FORMAT_ARGB8888           0
+#       define EVERGREEN_GRPH_FORMAT_ARGB2101010        1
+#       define EVERGREEN_GRPH_FORMAT_32BPP_DIG          2
+#       define EVERGREEN_GRPH_FORMAT_8B_ARGB2101010     3
+#       define EVERGREEN_GRPH_FORMAT_BGRA1010102        4
+#       define EVERGREEN_GRPH_FORMAT_8B_BGRA1010102     5
+#       define EVERGREEN_GRPH_FORMAT_RGB111110          6
+#       define EVERGREEN_GRPH_FORMAT_BGR101111          7
+#define EVERGREEN_GRPH_SWAP_CONTROL                     0x680c
+#       define EVERGREEN_GRPH_ENDIAN_SWAP(x)            (((x) & 0x3) << 0)
+#       define EVERGREEN_GRPH_ENDIAN_NONE               0
+#       define EVERGREEN_GRPH_ENDIAN_8IN16              1
+#       define EVERGREEN_GRPH_ENDIAN_8IN32              2
+#       define EVERGREEN_GRPH_ENDIAN_8IN64              3
+#       define EVERGREEN_GRPH_RED_CROSSBAR(x)           (((x) & 0x3) << 4)
+#       define EVERGREEN_GRPH_RED_SEL_R                 0
+#       define EVERGREEN_GRPH_RED_SEL_G                 1
+#       define EVERGREEN_GRPH_RED_SEL_B                 2
+#       define EVERGREEN_GRPH_RED_SEL_A                 3
+#       define EVERGREEN_GRPH_GREEN_CROSSBAR(x)         (((x) & 0x3) << 6)
+#       define EVERGREEN_GRPH_GREEN_SEL_G               0
+#       define EVERGREEN_GRPH_GREEN_SEL_B               1
+#       define EVERGREEN_GRPH_GREEN_SEL_A               2
+#       define EVERGREEN_GRPH_GREEN_SEL_R               3
+#       define EVERGREEN_GRPH_BLUE_CROSSBAR(x)          (((x) & 0x3) << 8)
+#       define EVERGREEN_GRPH_BLUE_SEL_B                0
+#       define EVERGREEN_GRPH_BLUE_SEL_A                1
+#       define EVERGREEN_GRPH_BLUE_SEL_R                2
+#       define EVERGREEN_GRPH_BLUE_SEL_G                3
+#       define EVERGREEN_GRPH_ALPHA_CROSSBAR(x)         (((x) & 0x3) << 10)
+#       define EVERGREEN_GRPH_ALPHA_SEL_A               0
+#       define EVERGREEN_GRPH_ALPHA_SEL_R               1
+#       define EVERGREEN_GRPH_ALPHA_SEL_G               2
+#       define EVERGREEN_GRPH_ALPHA_SEL_B               3
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS          0x6810
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS        0x6814
+#       define EVERGREEN_GRPH_DFQ_ENABLE                (1 << 0)
+#       define EVERGREEN_GRPH_SURFACE_ADDRESS_MASK      0xffffff00
+#define EVERGREEN_GRPH_PITCH                            0x6818
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH     0x681c
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH   0x6820
+#define EVERGREEN_GRPH_SURFACE_OFFSET_X                 0x6824
+#define EVERGREEN_GRPH_SURFACE_OFFSET_Y                 0x6828
+#define EVERGREEN_GRPH_X_START                          0x682c
+#define EVERGREEN_GRPH_Y_START                          0x6830
+#define EVERGREEN_GRPH_X_END                            0x6834
+#define EVERGREEN_GRPH_Y_END                            0x6838
+
+/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */
+#define EVERGREEN_CUR_CONTROL                           0x6998
+#       define EVERGREEN_CURSOR_EN                      (1 << 0)
+#       define EVERGREEN_CURSOR_MODE(x)                 (((x) & 0x3) << 8)
+#       define EVERGREEN_CURSOR_MONO                    0
+#       define EVERGREEN_CURSOR_24_1                    1
+#       define EVERGREEN_CURSOR_24_8_PRE_MULT           2
+#       define EVERGREEN_CURSOR_24_8_UNPRE_MULT         3
+#       define EVERGREEN_CURSOR_2X_MAGNIFY              (1 << 16)
+#       define EVERGREEN_CURSOR_FORCE_MC_ON             (1 << 20)
+#       define EVERGREEN_CURSOR_URGENT_CONTROL(x)       (((x) & 0x7) << 24)
+#       define EVERGREEN_CURSOR_URGENT_ALWAYS           0
+#       define EVERGREEN_CURSOR_URGENT_1_8              1
+#       define EVERGREEN_CURSOR_URGENT_1_4              2
+#       define EVERGREEN_CURSOR_URGENT_3_8              3
+#       define EVERGREEN_CURSOR_URGENT_1_2              4
+#define EVERGREEN_CUR_SURFACE_ADDRESS                   0x699c
+#       define EVERGREEN_CUR_SURFACE_ADDRESS_MASK       0xfffff000
+#define EVERGREEN_CUR_SIZE                              0x69a0
+#define EVERGREEN_CUR_SURFACE_ADDRESS_HIGH              0x69a4
+#define EVERGREEN_CUR_POSITION                          0x69a8
+#define EVERGREEN_CUR_HOT_SPOT                          0x69ac
+#define EVERGREEN_CUR_COLOR1                            0x69b0
+#define EVERGREEN_CUR_COLOR2                            0x69b4
+#define EVERGREEN_CUR_UPDATE                            0x69b8
+#       define EVERGREEN_CURSOR_UPDATE_PENDING          (1 << 0)
+#       define EVERGREEN_CURSOR_UPDATE_TAKEN            (1 << 1)
+#       define EVERGREEN_CURSOR_UPDATE_LOCK             (1 << 16)
+#       define EVERGREEN_CURSOR_DISABLE_MULTIPLE_UPDATE (1 << 24)
+
+/* LUT blocks at 0x69e0, 0x75e0, 0x101e0, 0x10de0, 0x119e0, 0x125e0 */
+#define EVERGREEN_DC_LUT_RW_MODE                        0x69e0
+#define EVERGREEN_DC_LUT_RW_INDEX                       0x69e4
+#define EVERGREEN_DC_LUT_SEQ_COLOR                      0x69e8
+#define EVERGREEN_DC_LUT_PWL_DATA                       0x69ec
+#define EVERGREEN_DC_LUT_30_COLOR                       0x69f0
+#define EVERGREEN_DC_LUT_VGA_ACCESS_ENABLE              0x69f4
+#define EVERGREEN_DC_LUT_WRITE_EN_MASK                  0x69f8
+#define EVERGREEN_DC_LUT_AUTOFILL                       0x69fc
+#define EVERGREEN_DC_LUT_CONTROL                        0x6a00
+#define EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE              0x6a04
+#define EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN             0x6a08
+#define EVERGREEN_DC_LUT_BLACK_OFFSET_RED               0x6a0c
+#define EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE              0x6a10
+#define EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN             0x6a14
+#define EVERGREEN_DC_LUT_WHITE_OFFSET_RED               0x6a18
+
+#define EVERGREEN_DATA_FORMAT                           0x6b00
+#       define EVERGREEN_INTERLEAVE_EN                  (1 << 0)
+#define EVERGREEN_DESKTOP_HEIGHT                        0x6b04
+
+#define EVERGREEN_VIEWPORT_START                        0x6d70
+#define EVERGREEN_VIEWPORT_SIZE                         0x6d74
+
+/* display controller offsets used for crtc/cur/lut/grph/viewport/etc. */
+#define EVERGREEN_CRTC0_REGISTER_OFFSET                 (0x6df0 - 0x6df0)
+#define EVERGREEN_CRTC1_REGISTER_OFFSET                 (0x79f0 - 0x6df0)
+#define EVERGREEN_CRTC2_REGISTER_OFFSET                 (0x105f0 - 0x6df0)
+#define EVERGREEN_CRTC3_REGISTER_OFFSET                 (0x111f0 - 0x6df0)
+#define EVERGREEN_CRTC4_REGISTER_OFFSET                 (0x11df0 - 0x6df0)
+#define EVERGREEN_CRTC5_REGISTER_OFFSET                 (0x129f0 - 0x6df0)
+
+/* CRTC blocks at 0x6df0, 0x79f0, 0x105f0, 0x111f0, 0x11df0, 0x129f0 */
+#define EVERGREEN_CRTC_CONTROL                          0x6e70
+#       define EVERGREEN_CRTC_MASTER_EN                 (1 << 0)
+#define EVERGREEN_CRTC_UPDATE_LOCK                      0x6ed4
+
+#define EVERGREEN_DC_GPIO_HPD_MASK                      0x64b0
+#define EVERGREEN_DC_GPIO_HPD_A                         0x64b4
+#define EVERGREEN_DC_GPIO_HPD_EN                        0x64b8
+#define EVERGREEN_DC_GPIO_HPD_Y                         0x64bc
+
+#endif
index c0d4650cdb794103a3fa7fe79d0a43c4806713c9..91eb762eb3f918625f3b17f0a8360551d628480e 100644 (file)
@@ -197,13 +197,13 @@ int r100_pci_gart_enable(struct radeon_device *rdev)
 {
        uint32_t tmp;
 
+       radeon_gart_restore(rdev);
        /* discard memory request outside of configured range */
        tmp = RREG32(RADEON_AIC_CNTL) | RADEON_DIS_OUT_OF_PCI_GART_ACCESS;
        WREG32(RADEON_AIC_CNTL, tmp);
        /* set address range for PCI address translate */
-       WREG32(RADEON_AIC_LO_ADDR, rdev->mc.gtt_location);
-       tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
-       WREG32(RADEON_AIC_HI_ADDR, tmp);
+       WREG32(RADEON_AIC_LO_ADDR, rdev->mc.gtt_start);
+       WREG32(RADEON_AIC_HI_ADDR, rdev->mc.gtt_end);
        /* set PCI GART page-table base address */
        WREG32(RADEON_AIC_PT_BASE, rdev->gart.table_addr);
        tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN;
@@ -312,9 +312,11 @@ int r100_irq_process(struct radeon_device *rdev)
                /* Vertical blank interrupts */
                if (status & RADEON_CRTC_VBLANK_STAT) {
                        drm_handle_vblank(rdev->ddev, 0);
+                       wake_up(&rdev->irq.vblank_queue);
                }
                if (status & RADEON_CRTC2_VBLANK_STAT) {
                        drm_handle_vblank(rdev->ddev, 1);
+                       wake_up(&rdev->irq.vblank_queue);
                }
                if (status & RADEON_FP_DETECT_STAT) {
                        queue_hotplug = true;
@@ -366,8 +368,8 @@ void r100_fence_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(rdev, PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0));
        radeon_ring_write(rdev, RADEON_RB3D_ZC_FLUSH_ALL);
        /* Wait until IDLE & CLEAN */
-       radeon_ring_write(rdev, PACKET0(0x1720, 0));
-       radeon_ring_write(rdev, (1 << 16) | (1 << 17));
+       radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
+       radeon_ring_write(rdev, RADEON_WAIT_2D_IDLECLEAN | RADEON_WAIT_3D_IDLECLEAN);
        radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
        radeon_ring_write(rdev, rdev->config.r100.hdp_cntl |
                                RADEON_HDP_READ_BUFFER_INVALIDATE);
@@ -1701,7 +1703,7 @@ int r100_gui_wait_for_idle(struct radeon_device *rdev)
        }
        for (i = 0; i < rdev->usec_timeout; i++) {
                tmp = RREG32(RADEON_RBBM_STATUS);
-               if (!(tmp & (1 << 31))) {
+               if (!(tmp & RADEON_RBBM_ACTIVE)) {
                        return 0;
                }
                DRM_UDELAY(1);
@@ -1716,8 +1718,8 @@ int r100_mc_wait_for_idle(struct radeon_device *rdev)
 
        for (i = 0; i < rdev->usec_timeout; i++) {
                /* read MC_STATUS */
-               tmp = RREG32(0x0150);
-               if (tmp & (1 << 2)) {
+               tmp = RREG32(RADEON_MC_STATUS);
+               if (tmp & RADEON_MC_IDLE) {
                        return 0;
                }
                DRM_UDELAY(1);
@@ -1790,7 +1792,7 @@ int r100_gpu_reset(struct radeon_device *rdev)
        }
        /* Check if GPU is idle */
        status = RREG32(RADEON_RBBM_STATUS);
-       if (status & (1 << 31)) {
+       if (status & RADEON_RBBM_ACTIVE) {
                DRM_ERROR("Failed to reset GPU (RBBM_STATUS=0x%08X)\n", status);
                return -1;
        }
@@ -1800,6 +1802,9 @@ int r100_gpu_reset(struct radeon_device *rdev)
 
 void r100_set_common_regs(struct radeon_device *rdev)
 {
+       struct drm_device *dev = rdev->ddev;
+       bool force_dac2 = false;
+
        /* set these so they don't interfere with anything */
        WREG32(RADEON_OV0_SCALE_CNTL, 0);
        WREG32(RADEON_SUBPIC_CNTL, 0);
@@ -1808,6 +1813,68 @@ void r100_set_common_regs(struct radeon_device *rdev)
        WREG32(RADEON_DVI_I2C_CNTL_1, 0);
        WREG32(RADEON_CAP0_TRIG_CNTL, 0);
        WREG32(RADEON_CAP1_TRIG_CNTL, 0);
+
+       /* always set up dac2 on rn50 and some rv100 as lots
+        * of servers seem to wire it up to a VGA port but
+        * don't report it in the bios connector
+        * table.
+        */
+       switch (dev->pdev->device) {
+               /* RN50 */
+       case 0x515e:
+       case 0x5969:
+               force_dac2 = true;
+               break;
+               /* RV100*/
+       case 0x5159:
+       case 0x515a:
+               /* DELL triple head servers */
+               if ((dev->pdev->subsystem_vendor == 0x1028 /* DELL */) &&
+                   ((dev->pdev->subsystem_device == 0x016c) ||
+                    (dev->pdev->subsystem_device == 0x016d) ||
+                    (dev->pdev->subsystem_device == 0x016e) ||
+                    (dev->pdev->subsystem_device == 0x016f) ||
+                    (dev->pdev->subsystem_device == 0x0170) ||
+                    (dev->pdev->subsystem_device == 0x017d) ||
+                    (dev->pdev->subsystem_device == 0x017e) ||
+                    (dev->pdev->subsystem_device == 0x0183) ||
+                    (dev->pdev->subsystem_device == 0x018a) ||
+                    (dev->pdev->subsystem_device == 0x019a)))
+                       force_dac2 = true;
+               break;
+       }
+
+       if (force_dac2) {
+               u32 disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
+               u32 tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
+               u32 dac2_cntl = RREG32(RADEON_DAC_CNTL2);
+
+               /* For CRT on DAC2, don't turn it on if BIOS didn't
+                  enable it, even it's detected.
+               */
+
+               /* force it to crtc0 */
+               dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
+               dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
+               disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
+
+               /* set up the TV DAC */
+               tv_dac_cntl &= ~(RADEON_TV_DAC_PEDESTAL |
+                                RADEON_TV_DAC_STD_MASK |
+                                RADEON_TV_DAC_RDACPD |
+                                RADEON_TV_DAC_GDACPD |
+                                RADEON_TV_DAC_BDACPD |
+                                RADEON_TV_DAC_BGADJ_MASK |
+                                RADEON_TV_DAC_DACADJ_MASK);
+               tv_dac_cntl |= (RADEON_TV_DAC_NBLANK |
+                               RADEON_TV_DAC_NHOLD |
+                               RADEON_TV_DAC_STD_PS2 |
+                               (0x58 << 16));
+
+               WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
+               WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
+               WREG32(RADEON_DAC_CNTL2, dac2_cntl);
+       }
 }
 
 /*
@@ -1889,17 +1956,20 @@ static u32 r100_get_accessible_vram(struct radeon_device *rdev)
 void r100_vram_init_sizes(struct radeon_device *rdev)
 {
        u64 config_aper_size;
-       u32 accessible;
 
+       /* work out accessible VRAM */
+       rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
+       rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
+       rdev->mc.visible_vram_size = r100_get_accessible_vram(rdev);
+       /* FIXME we don't use the second aperture yet when we could use it */
+       if (rdev->mc.visible_vram_size > rdev->mc.aper_size)
+               rdev->mc.visible_vram_size = rdev->mc.aper_size;
        config_aper_size = RREG32(RADEON_CONFIG_APER_SIZE);
-
        if (rdev->flags & RADEON_IS_IGP) {
                uint32_t tom;
                /* read NB_TOM to get the amount of ram stolen for the GPU */
                tom = RREG32(RADEON_NB_TOM);
                rdev->mc.real_vram_size = (((tom >> 16) - (tom & 0xffff) + 1) << 16);
-               /* for IGPs we need to keep VRAM where it was put by the BIOS */
-               rdev->mc.vram_location = (tom & 0xffff) << 16;
                WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size);
                rdev->mc.mc_vram_size = rdev->mc.real_vram_size;
        } else {
@@ -1911,30 +1981,19 @@ void r100_vram_init_sizes(struct radeon_device *rdev)
                        rdev->mc.real_vram_size = 8192 * 1024;
                        WREG32(RADEON_CONFIG_MEMSIZE, rdev->mc.real_vram_size);
                }
-               /* let driver place VRAM */
-               rdev->mc.vram_location = 0xFFFFFFFFUL;
-                /* Fix for RN50, M6, M7 with 8/16/32(??) MBs of VRAM - 
-                 * Novell bug 204882 + along with lots of ubuntu ones */
+               /* Fix for RN50, M6, M7 with 8/16/32(??) MBs of VRAM - 
+                * Novell bug 204882 + along with lots of ubuntu ones
+                */
                if (config_aper_size > rdev->mc.real_vram_size)
                        rdev->mc.mc_vram_size = config_aper_size;
                else
                        rdev->mc.mc_vram_size = rdev->mc.real_vram_size;
        }
-
-       /* work out accessible VRAM */
-       accessible = r100_get_accessible_vram(rdev);
-
-       rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
-       rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
-
-       if (accessible > rdev->mc.aper_size)
-               accessible = rdev->mc.aper_size;
-
-       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
+       /* FIXME remove this once we support unmappable VRAM */
+       if (rdev->mc.mc_vram_size > rdev->mc.aper_size) {
                rdev->mc.mc_vram_size = rdev->mc.aper_size;
-
-       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
                rdev->mc.real_vram_size = rdev->mc.aper_size;
+       }
 }
 
 void r100_vga_set_state(struct radeon_device *rdev, bool state)
@@ -1951,11 +2010,18 @@ void r100_vga_set_state(struct radeon_device *rdev, bool state)
        WREG32(RADEON_CONFIG_CNTL, temp);
 }
 
-void r100_vram_info(struct radeon_device *rdev)
+void r100_mc_init(struct radeon_device *rdev)
 {
-       r100_vram_get_type(rdev);
+       u64 base;
 
+       r100_vram_get_type(rdev);
        r100_vram_init_sizes(rdev);
+       base = rdev->mc.aper_base;
+       if (rdev->flags & RADEON_IS_IGP)
+               base = (RREG32(RADEON_NB_TOM) & 0xffff) << 16;
+       radeon_vram_location(rdev, &rdev->mc, base);
+       if (!(rdev->flags & RADEON_IS_AGP))
+               radeon_gtt_location(rdev, &rdev->mc);
 }
 
 
@@ -3226,10 +3292,9 @@ void r100_mc_stop(struct radeon_device *rdev, struct r100_mc_save *save)
 void r100_mc_resume(struct radeon_device *rdev, struct r100_mc_save *save)
 {
        /* Update base address for crtc */
-       WREG32(R_00023C_DISPLAY_BASE_ADDR, rdev->mc.vram_location);
+       WREG32(R_00023C_DISPLAY_BASE_ADDR, rdev->mc.vram_start);
        if (!(rdev->flags & RADEON_SINGLE_CRTC)) {
-               WREG32(R_00033C_CRTC2_DISPLAY_BASE_ADDR,
-                               rdev->mc.vram_location);
+               WREG32(R_00033C_CRTC2_DISPLAY_BASE_ADDR, rdev->mc.vram_start);
        }
        /* Restore CRTC registers */
        WREG8(R_0003C2_GENMO_WT, save->GENMO_WT);
@@ -3390,32 +3455,6 @@ void r100_fini(struct radeon_device *rdev)
        rdev->bios = NULL;
 }
 
-int r100_mc_init(struct radeon_device *rdev)
-{
-       int r;
-       u32 tmp;
-
-       /* Setup GPU memory space */
-       rdev->mc.vram_location = 0xFFFFFFFFUL;
-       rdev->mc.gtt_location = 0xFFFFFFFFUL;
-       if (rdev->flags & RADEON_IS_IGP) {
-               tmp = G_00015C_MC_FB_START(RREG32(R_00015C_NB_TOM));
-               rdev->mc.vram_location = tmp << 16;
-       }
-       if (rdev->flags & RADEON_IS_AGP) {
-               r = radeon_agp_init(rdev);
-               if (r) {
-                       radeon_agp_disable(rdev);
-               } else {
-                       rdev->mc.gtt_location = rdev->mc.agp_base;
-               }
-       }
-       r = radeon_mc_setup(rdev);
-       if (r)
-               return r;
-       return 0;
-}
-
 int r100_init(struct radeon_device *rdev)
 {
        int r;
@@ -3458,12 +3497,15 @@ int r100_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       r100_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = r100_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize AGP */
+       if (rdev->flags & RADEON_IS_AGP) {
+               r = radeon_agp_init(rdev);
+               if (r) {
+                       radeon_agp_disable(rdev);
+               }
+       }
+       /* initialize VRAM */
+       r100_mc_init(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
        if (r)
index ff1e0cd608bf11d23981511f017782b4fa383be8..1146c9909c2c11457f583edf73e8b5c31e0ab5b1 100644 (file)
@@ -31,6 +31,7 @@
 #include "radeon_reg.h"
 #include "radeon.h"
 
+#include "r100d.h"
 #include "r200_reg_safe.h"
 
 #include "r100_track.h"
@@ -79,6 +80,51 @@ static int r200_get_vtx_size_0(uint32_t vtx_fmt_0)
        return vtx_size;
 }
 
+int r200_copy_dma(struct radeon_device *rdev,
+                 uint64_t src_offset,
+                 uint64_t dst_offset,
+                 unsigned num_pages,
+                 struct radeon_fence *fence)
+{
+       uint32_t size;
+       uint32_t cur_size;
+       int i, num_loops;
+       int r = 0;
+
+       /* radeon pitch is /64 */
+       size = num_pages << PAGE_SHIFT;
+       num_loops = DIV_ROUND_UP(size, 0x1FFFFF);
+       r = radeon_ring_lock(rdev, num_loops * 4 + 64);
+       if (r) {
+               DRM_ERROR("radeon: moving bo (%d).\n", r);
+               return r;
+       }
+       /* Must wait for 2D idle & clean before DMA or hangs might happen */
+       radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
+       radeon_ring_write(rdev, (1 << 16));
+       for (i = 0; i < num_loops; i++) {
+               cur_size = size;
+               if (cur_size > 0x1FFFFF) {
+                       cur_size = 0x1FFFFF;
+               }
+               size -= cur_size;
+               radeon_ring_write(rdev, PACKET0(0x720, 2));
+               radeon_ring_write(rdev, src_offset);
+               radeon_ring_write(rdev, dst_offset);
+               radeon_ring_write(rdev, cur_size | (1 << 31) | (1 << 30));
+               src_offset += cur_size;
+               dst_offset += cur_size;
+       }
+       radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
+       radeon_ring_write(rdev, RADEON_WAIT_DMA_GUI_IDLE);
+       if (fence) {
+               r = radeon_fence_emit(rdev, fence);
+       }
+       radeon_ring_unlock_commit(rdev);
+       return r;
+}
+
+
 static int r200_get_vtx_size_1(uint32_t vtx_fmt_1)
 {
        int vtx_size, i, tex_size;
index 43b55a030b4d5c35ec86e3b79cb86e01314ee67e..4cef90cd74e5176fe01b41b37eb38d1050db1fb4 100644 (file)
@@ -117,18 +117,19 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
        r = radeon_gart_table_vram_pin(rdev);
        if (r)
                return r;
+       radeon_gart_restore(rdev);
        /* discard memory request outside of configured range */
        tmp = RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
        WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp);
-       WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, rdev->mc.gtt_location);
-       tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - RADEON_GPU_PAGE_SIZE;
+       WREG32_PCIE(RADEON_PCIE_TX_GART_START_LO, rdev->mc.gtt_start);
+       tmp = rdev->mc.gtt_end & ~RADEON_GPU_PAGE_MASK;
        WREG32_PCIE(RADEON_PCIE_TX_GART_END_LO, tmp);
        WREG32_PCIE(RADEON_PCIE_TX_GART_START_HI, 0);
        WREG32_PCIE(RADEON_PCIE_TX_GART_END_HI, 0);
        table_addr = rdev->gart.table_addr;
        WREG32_PCIE(RADEON_PCIE_TX_GART_BASE, table_addr);
        /* FIXME: setup default page */
-       WREG32_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_LO, rdev->mc.vram_location);
+       WREG32_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_LO, rdev->mc.vram_start);
        WREG32_PCIE(RADEON_PCIE_TX_DISCARD_RD_ADDR_HI, 0);
        /* Clear error */
        WREG32_PCIE(0x18, 0);
@@ -174,18 +175,20 @@ void r300_fence_ring_emit(struct radeon_device *rdev,
        /* Who ever call radeon_fence_emit should call ring_lock and ask
         * for enough space (today caller are ib schedule and buffer move) */
        /* Write SC register so SC & US assert idle */
-       radeon_ring_write(rdev, PACKET0(0x43E0, 0));
+       radeon_ring_write(rdev, PACKET0(R300_RE_SCISSORS_TL, 0));
        radeon_ring_write(rdev, 0);
-       radeon_ring_write(rdev, PACKET0(0x43E4, 0));
+       radeon_ring_write(rdev, PACKET0(R300_RE_SCISSORS_BR, 0));
        radeon_ring_write(rdev, 0);
        /* Flush 3D cache */
-       radeon_ring_write(rdev, PACKET0(0x4E4C, 0));
-       radeon_ring_write(rdev, (2 << 0));
-       radeon_ring_write(rdev, PACKET0(0x4F18, 0));
-       radeon_ring_write(rdev, (1 << 0));
+       radeon_ring_write(rdev, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0));
+       radeon_ring_write(rdev, R300_RB3D_DC_FLUSH);
+       radeon_ring_write(rdev, PACKET0(R300_RB3D_ZCACHE_CTLSTAT, 0));
+       radeon_ring_write(rdev, R300_ZC_FLUSH);
        /* Wait until IDLE & CLEAN */
-       radeon_ring_write(rdev, PACKET0(0x1720, 0));
-       radeon_ring_write(rdev, (1 << 17) | (1 << 16)  | (1 << 9));
+       radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
+       radeon_ring_write(rdev, (RADEON_WAIT_3D_IDLECLEAN |
+                                RADEON_WAIT_2D_IDLECLEAN |
+                                RADEON_WAIT_DMA_GUI_IDLE));
        radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
        radeon_ring_write(rdev, rdev->config.r300.hdp_cntl |
                                RADEON_HDP_READ_BUFFER_INVALIDATE);
@@ -198,50 +201,6 @@ void r300_fence_ring_emit(struct radeon_device *rdev,
        radeon_ring_write(rdev, RADEON_SW_INT_FIRE);
 }
 
-int r300_copy_dma(struct radeon_device *rdev,
-                 uint64_t src_offset,
-                 uint64_t dst_offset,
-                 unsigned num_pages,
-                 struct radeon_fence *fence)
-{
-       uint32_t size;
-       uint32_t cur_size;
-       int i, num_loops;
-       int r = 0;
-
-       /* radeon pitch is /64 */
-       size = num_pages << PAGE_SHIFT;
-       num_loops = DIV_ROUND_UP(size, 0x1FFFFF);
-       r = radeon_ring_lock(rdev, num_loops * 4 + 64);
-       if (r) {
-               DRM_ERROR("radeon: moving bo (%d).\n", r);
-               return r;
-       }
-       /* Must wait for 2D idle & clean before DMA or hangs might happen */
-       radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0 ));
-       radeon_ring_write(rdev, (1 << 16));
-       for (i = 0; i < num_loops; i++) {
-               cur_size = size;
-               if (cur_size > 0x1FFFFF) {
-                       cur_size = 0x1FFFFF;
-               }
-               size -= cur_size;
-               radeon_ring_write(rdev, PACKET0(0x720, 2));
-               radeon_ring_write(rdev, src_offset);
-               radeon_ring_write(rdev, dst_offset);
-               radeon_ring_write(rdev, cur_size | (1 << 31) | (1 << 30));
-               src_offset += cur_size;
-               dst_offset += cur_size;
-       }
-       radeon_ring_write(rdev, PACKET0(RADEON_WAIT_UNTIL, 0));
-       radeon_ring_write(rdev, RADEON_WAIT_DMA_GUI_IDLE);
-       if (fence) {
-               r = radeon_fence_emit(rdev, fence);
-       }
-       radeon_ring_unlock_commit(rdev);
-       return r;
-}
-
 void r300_ring_start(struct radeon_device *rdev)
 {
        unsigned gb_tile_config;
@@ -281,8 +240,8 @@ void r300_ring_start(struct radeon_device *rdev)
        radeon_ring_write(rdev,
                          RADEON_WAIT_2D_IDLECLEAN |
                          RADEON_WAIT_3D_IDLECLEAN);
-       radeon_ring_write(rdev, PACKET0(0x170C, 0));
-       radeon_ring_write(rdev, 1 << 31);
+       radeon_ring_write(rdev, PACKET0(R300_DST_PIPE_CONFIG, 0));
+       radeon_ring_write(rdev, R300_PIPE_AUTO_CONFIG);
        radeon_ring_write(rdev, PACKET0(R300_GB_SELECT, 0));
        radeon_ring_write(rdev, 0);
        radeon_ring_write(rdev, PACKET0(R300_GB_ENABLE, 0));
@@ -349,8 +308,8 @@ int r300_mc_wait_for_idle(struct radeon_device *rdev)
 
        for (i = 0; i < rdev->usec_timeout; i++) {
                /* read MC_STATUS */
-               tmp = RREG32(0x0150);
-               if (tmp & (1 << 4)) {
+               tmp = RREG32(RADEON_MC_STATUS);
+               if (tmp & R300_MC_IDLE) {
                        return 0;
                }
                DRM_UDELAY(1);
@@ -395,8 +354,8 @@ void r300_gpu_init(struct radeon_device *rdev)
                       "programming pipes. Bad things might happen.\n");
        }
 
-       tmp = RREG32(0x170C);
-       WREG32(0x170C, tmp | (1 << 31));
+       tmp = RREG32(R300_DST_PIPE_CONFIG);
+       WREG32(R300_DST_PIPE_CONFIG, tmp | R300_PIPE_AUTO_CONFIG);
 
        WREG32(R300_RB2D_DSTCACHE_MODE,
               R300_DC_AUTOFLUSH_ENABLE |
@@ -437,8 +396,8 @@ int r300_ga_reset(struct radeon_device *rdev)
                        /* GA still busy soft reset it */
                        WREG32(0x429C, 0x200);
                        WREG32(R300_VAP_PVS_STATE_FLUSH_REG, 0);
-                       WREG32(0x43E0, 0);
-                       WREG32(0x43E4, 0);
+                       WREG32(R300_RE_SCISSORS_TL, 0);
+                       WREG32(R300_RE_SCISSORS_BR, 0);
                        WREG32(0x24AC, 0);
                }
                /* Wait to prevent race in RBBM_STATUS */
@@ -488,7 +447,7 @@ int r300_gpu_reset(struct radeon_device *rdev)
        }
        /* Check if GPU is idle */
        status = RREG32(RADEON_RBBM_STATUS);
-       if (status & (1 << 31)) {
+       if (status & RADEON_RBBM_ACTIVE) {
                DRM_ERROR("Failed to reset GPU (RBBM_STATUS=0x%08X)\n", status);
                return -1;
        }
@@ -500,13 +459,13 @@ int r300_gpu_reset(struct radeon_device *rdev)
 /*
  * r300,r350,rv350,rv380 VRAM info
  */
-void r300_vram_info(struct radeon_device *rdev)
+void r300_mc_init(struct radeon_device *rdev)
 {
-       uint32_t tmp;
+       u64 base;
+       u32 tmp;
 
        /* DDR for all card after R300 & IGP */
        rdev->mc.vram_is_ddr = true;
-
        tmp = RREG32(RADEON_MEM_CNTL);
        tmp &= R300_MEM_NUM_CHANNELS_MASK;
        switch (tmp) {
@@ -515,8 +474,13 @@ void r300_vram_info(struct radeon_device *rdev)
        case 2: rdev->mc.vram_width = 256; break;
        default:  rdev->mc.vram_width = 128; break;
        }
-
        r100_vram_init_sizes(rdev);
+       base = rdev->mc.aper_base;
+       if (rdev->flags & RADEON_IS_IGP)
+               base = (RREG32(RADEON_NB_TOM) & 0xffff) << 16;
+       radeon_vram_location(rdev, &rdev->mc, base);
+       if (!(rdev->flags & RADEON_IS_AGP))
+               radeon_gtt_location(rdev, &rdev->mc);
 }
 
 void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes)
@@ -578,6 +542,40 @@ void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes)
 
 }
 
+int rv370_get_pcie_lanes(struct radeon_device *rdev)
+{
+       u32 link_width_cntl;
+
+       if (rdev->flags & RADEON_IS_IGP)
+               return 0;
+
+       if (!(rdev->flags & RADEON_IS_PCIE))
+               return 0;
+
+       /* FIXME wait for idle */
+
+       if (rdev->family < CHIP_R600)
+               link_width_cntl = RREG32_PCIE(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
+       else
+               link_width_cntl = RREG32_PCIE_P(RADEON_PCIE_LC_LINK_WIDTH_CNTL);
+
+       switch ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) >> RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT) {
+       case RADEON_PCIE_LC_LINK_WIDTH_X0:
+               return 0;
+       case RADEON_PCIE_LC_LINK_WIDTH_X1:
+               return 1;
+       case RADEON_PCIE_LC_LINK_WIDTH_X2:
+               return 2;
+       case RADEON_PCIE_LC_LINK_WIDTH_X4:
+               return 4;
+       case RADEON_PCIE_LC_LINK_WIDTH_X8:
+               return 8;
+       case RADEON_PCIE_LC_LINK_WIDTH_X16:
+       default:
+               return 16;
+       }
+}
+
 #if defined(CONFIG_DEBUG_FS)
 static int rv370_debugfs_pcie_gart_info(struct seq_file *m, void *data)
 {
@@ -707,6 +705,8 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
                        tile_flags |= R300_TXO_MACRO_TILE;
                if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
                        tile_flags |= R300_TXO_MICRO_TILE;
+               else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+                       tile_flags |= R300_TXO_MICRO_TILE_SQUARE;
 
                tmp = idx_value + ((u32)reloc->lobj.gpu_offset);
                tmp |= tile_flags;
@@ -757,6 +757,8 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
                        tile_flags |= R300_COLOR_TILE_ENABLE;
                if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
                        tile_flags |= R300_COLOR_MICROTILE_ENABLE;
+               else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+                       tile_flags |= R300_COLOR_MICROTILE_SQUARE_ENABLE;
 
                tmp = idx_value & ~(0x7 << 16);
                tmp |= tile_flags;
@@ -828,7 +830,9 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
                if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
                        tile_flags |= R300_DEPTHMACROTILE_ENABLE;
                if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
-                       tile_flags |= R300_DEPTHMICROTILE_TILED;;
+                       tile_flags |= R300_DEPTHMICROTILE_TILED;
+               else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO_SQUARE)
+                       tile_flags |= R300_DEPTHMICROTILE_TILED_SQUARE;
 
                tmp = idx_value & ~(0x7 << 16);
                tmp |= tile_flags;
@@ -1387,12 +1391,15 @@ int r300_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       r300_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = r420_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize AGP */
+       if (rdev->flags & RADEON_IS_AGP) {
+               r = radeon_agp_init(rdev);
+               if (r) {
+                       radeon_agp_disable(rdev);
+               }
+       }
+       /* initialize memory controller */
+       r300_mc_init(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
        if (r)
index 34bffa0e4b73acc358598c489fd0549ec275a918..ea46d558e8f394040cd9d7b3503c8c3c2e1be5da 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "drmP.h"
 #include "drm.h"
+#include "drm_buffer.h"
 #include "radeon_drm.h"
 #include "radeon_drv.h"
 #include "r300_reg.h"
@@ -299,46 +300,42 @@ static __inline__ int r300_emit_carefully_checked_packet0(drm_radeon_private_t *
        int reg;
        int sz;
        int i;
-       int values[64];
+       u32 *value;
        RING_LOCALS;
 
        sz = header.packet0.count;
        reg = (header.packet0.reghi << 8) | header.packet0.reglo;
 
        if ((sz > 64) || (sz < 0)) {
-               DRM_ERROR
-                   ("Cannot emit more than 64 values at a time (reg=%04x sz=%d)\n",
-                    reg, sz);
+               DRM_ERROR("Cannot emit more than 64 values at a time (reg=%04x sz=%d)\n",
+                        reg, sz);
                return -EINVAL;
        }
+
        for (i = 0; i < sz; i++) {
-               values[i] = ((int *)cmdbuf->buf)[i];
                switch (r300_reg_flags[(reg >> 2) + i]) {
                case MARK_SAFE:
                        break;
                case MARK_CHECK_OFFSET:
-                       if (!radeon_check_offset(dev_priv, (u32) values[i])) {
-                               DRM_ERROR
-                                   ("Offset failed range check (reg=%04x sz=%d)\n",
-                                    reg, sz);
+                       value = drm_buffer_pointer_to_dword(cmdbuf->buffer, i);
+                       if (!radeon_check_offset(dev_priv, *value)) {
+                               DRM_ERROR("Offset failed range check (reg=%04x sz=%d)\n",
+                                        reg, sz);
                                return -EINVAL;
                        }
                        break;
                default:
                        DRM_ERROR("Register %04x failed check as flag=%02x\n",
-                                 reg + i * 4, r300_reg_flags[(reg >> 2) + i]);
+                               reg + i * 4, r300_reg_flags[(reg >> 2) + i]);
                        return -EINVAL;
                }
        }
 
        BEGIN_RING(1 + sz);
        OUT_RING(CP_PACKET0(reg, sz - 1));
-       OUT_RING_TABLE(values, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * 4;
-       cmdbuf->bufsz -= sz * 4;
-
        return 0;
 }
 
@@ -362,7 +359,7 @@ static __inline__ int r300_emit_packet0(drm_radeon_private_t *dev_priv,
        if (!sz)
                return 0;
 
-       if (sz * 4 > cmdbuf->bufsz)
+       if (sz * 4 > drm_buffer_unprocessed(cmdbuf->buffer))
                return -EINVAL;
 
        if (reg + sz * 4 >= 0x10000) {
@@ -380,12 +377,9 @@ static __inline__ int r300_emit_packet0(drm_radeon_private_t *dev_priv,
 
        BEGIN_RING(1 + sz);
        OUT_RING(CP_PACKET0(reg, sz - 1));
-       OUT_RING_TABLE((int *)cmdbuf->buf, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * 4;
-       cmdbuf->bufsz -= sz * 4;
-
        return 0;
 }
 
@@ -407,7 +401,7 @@ static __inline__ int r300_emit_vpu(drm_radeon_private_t *dev_priv,
 
        if (!sz)
                return 0;
-       if (sz * 16 > cmdbuf->bufsz)
+       if (sz * 16 > drm_buffer_unprocessed(cmdbuf->buffer))
                return -EINVAL;
 
        /* VAP is very sensitive so we purge cache before we program it
@@ -426,7 +420,7 @@ static __inline__ int r300_emit_vpu(drm_radeon_private_t *dev_priv,
        BEGIN_RING(3 + sz * 4);
        OUT_RING_REG(R300_VAP_PVS_UPLOAD_ADDRESS, addr);
        OUT_RING(CP_PACKET0_TABLE(R300_VAP_PVS_UPLOAD_DATA, sz * 4 - 1));
-       OUT_RING_TABLE((int *)cmdbuf->buf, sz * 4);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz * 4);
        ADVANCE_RING();
 
        BEGIN_RING(2);
@@ -434,9 +428,6 @@ static __inline__ int r300_emit_vpu(drm_radeon_private_t *dev_priv,
        OUT_RING(0);
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * 16;
-       cmdbuf->bufsz -= sz * 16;
-
        return 0;
 }
 
@@ -449,14 +440,14 @@ static __inline__ int r300_emit_clear(drm_radeon_private_t *dev_priv,
 {
        RING_LOCALS;
 
-       if (8 * 4 > cmdbuf->bufsz)
+       if (8 * 4 > drm_buffer_unprocessed(cmdbuf->buffer))
                return -EINVAL;
 
        BEGIN_RING(10);
        OUT_RING(CP_PACKET3(R200_3D_DRAW_IMMD_2, 8));
        OUT_RING(R300_PRIM_TYPE_POINT | R300_PRIM_WALK_RING |
                 (1 << R300_PRIM_NUM_VERTICES_SHIFT));
-       OUT_RING_TABLE((int *)cmdbuf->buf, 8);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, 8);
        ADVANCE_RING();
 
        BEGIN_RING(4);
@@ -468,9 +459,6 @@ static __inline__ int r300_emit_clear(drm_radeon_private_t *dev_priv,
        /* set flush flag */
        dev_priv->track_flush |= RADEON_FLUSH_EMITED;
 
-       cmdbuf->buf += 8 * 4;
-       cmdbuf->bufsz -= 8 * 4;
-
        return 0;
 }
 
@@ -480,28 +468,29 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv,
 {
        int count, i, k;
 #define MAX_ARRAY_PACKET  64
-       u32 payload[MAX_ARRAY_PACKET];
+       u32 *data;
        u32 narrays;
        RING_LOCALS;
 
-       count = (header >> 16) & 0x3fff;
+       count = (header & RADEON_CP_PACKET_COUNT_MASK) >> 16;
 
        if ((count + 1) > MAX_ARRAY_PACKET) {
                DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n",
                          count);
                return -EINVAL;
        }
-       memset(payload, 0, MAX_ARRAY_PACKET * 4);
-       memcpy(payload, cmdbuf->buf + 4, (count + 1) * 4);
-
        /* carefully check packet contents */
 
-       narrays = payload[0];
+       /* We have already read the header so advance the buffer. */
+       drm_buffer_advance(cmdbuf->buffer, 4);
+
+       narrays = *(u32 *)drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
        k = 0;
        i = 1;
        while ((k < narrays) && (i < (count + 1))) {
                i++;            /* skip attribute field */
-               if (!radeon_check_offset(dev_priv, payload[i])) {
+               data = drm_buffer_pointer_to_dword(cmdbuf->buffer, i);
+               if (!radeon_check_offset(dev_priv, *data)) {
                        DRM_ERROR
                            ("Offset failed range check (k=%d i=%d) while processing 3D_LOAD_VBPNTR packet.\n",
                             k, i);
@@ -512,7 +501,8 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv,
                if (k == narrays)
                        break;
                /* have one more to process, they come in pairs */
-               if (!radeon_check_offset(dev_priv, payload[i])) {
+               data = drm_buffer_pointer_to_dword(cmdbuf->buffer, i);
+               if (!radeon_check_offset(dev_priv, *data)) {
                        DRM_ERROR
                            ("Offset failed range check (k=%d i=%d) while processing 3D_LOAD_VBPNTR packet.\n",
                             k, i);
@@ -533,30 +523,30 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv,
 
        BEGIN_RING(count + 2);
        OUT_RING(header);
-       OUT_RING_TABLE(payload, count + 1);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 1);
        ADVANCE_RING();
 
-       cmdbuf->buf += (count + 2) * 4;
-       cmdbuf->bufsz -= (count + 2) * 4;
-
        return 0;
 }
 
 static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv,
                                             drm_radeon_kcmd_buffer_t *cmdbuf)
 {
-       u32 *cmd = (u32 *) cmdbuf->buf;
+       u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
        int count, ret;
        RING_LOCALS;
 
-       count=(cmd[0]>>16) & 0x3fff;
 
-       if (cmd[0] & 0x8000) {
-               u32 offset;
+       count = (*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16;
 
-               if (cmd[1] & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL
+       if (*cmd & 0x8000) {
+               u32 offset;
+               u32 *cmd1 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
+               if (*cmd1 & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL
                              | RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
-                       offset = cmd[2] << 10;
+
+                       u32 *cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2);
+                       offset = *cmd2 << 10;
                        ret = !radeon_check_offset(dev_priv, offset);
                        if (ret) {
                                DRM_ERROR("Invalid bitblt first offset is %08X\n", offset);
@@ -564,9 +554,10 @@ static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv,
                        }
                }
 
-               if ((cmd[1] & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) &&
-                   (cmd[1] & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
-                       offset = cmd[3] << 10;
+               if ((*cmd1 & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) &&
+                   (*cmd1 & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
+                       u32 *cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3);
+                       offset = *cmd3 << 10;
                        ret = !radeon_check_offset(dev_priv, offset);
                        if (ret) {
                                DRM_ERROR("Invalid bitblt second offset is %08X\n", offset);
@@ -577,28 +568,25 @@ static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv,
        }
 
        BEGIN_RING(count+2);
-       OUT_RING(cmd[0]);
-       OUT_RING_TABLE((int *)(cmdbuf->buf + 4), count + 1);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 2);
        ADVANCE_RING();
 
-       cmdbuf->buf += (count+2)*4;
-       cmdbuf->bufsz -= (count+2)*4;
-
        return 0;
 }
 
 static __inline__ int r300_emit_draw_indx_2(drm_radeon_private_t *dev_priv,
                                            drm_radeon_kcmd_buffer_t *cmdbuf)
 {
-       u32 *cmd;
+       u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
+       u32 *cmd1 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
        int count;
        int expected_count;
        RING_LOCALS;
 
-       cmd = (u32 *) cmdbuf->buf;
-       count = (cmd[0]>>16) & 0x3fff;
-       expected_count = cmd[1] >> 16;
-       if (!(cmd[1] & R300_VAP_VF_CNTL__INDEX_SIZE_32bit))
+       count = (*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16;
+
+       expected_count = *cmd1 >> 16;
+       if (!(*cmd1 & R300_VAP_VF_CNTL__INDEX_SIZE_32bit))
                expected_count = (expected_count+1)/2;
 
        if (count && count != expected_count) {
@@ -608,55 +596,53 @@ static __inline__ int r300_emit_draw_indx_2(drm_radeon_private_t *dev_priv,
        }
 
        BEGIN_RING(count+2);
-       OUT_RING(cmd[0]);
-       OUT_RING_TABLE((int *)(cmdbuf->buf + 4), count + 1);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 2);
        ADVANCE_RING();
 
-       cmdbuf->buf += (count+2)*4;
-       cmdbuf->bufsz -= (count+2)*4;
-
        if (!count) {
-               drm_r300_cmd_header_t header;
+               drm_r300_cmd_header_t stack_header, *header;
+               u32 *cmd1, *cmd2, *cmd3;
 
-               if (cmdbuf->bufsz < 4*4 + sizeof(header)) {
+               if (drm_buffer_unprocessed(cmdbuf->buffer)
+                               < 4*4 + sizeof(stack_header)) {
                        DRM_ERROR("3D_DRAW_INDX_2: expect subsequent INDX_BUFFER, but stream is too short.\n");
                        return -EINVAL;
                }
 
-               header.u = *(unsigned int *)cmdbuf->buf;
+               header = drm_buffer_read_object(cmdbuf->buffer,
+                               sizeof(stack_header), &stack_header);
 
-               cmdbuf->buf += sizeof(header);
-               cmdbuf->bufsz -= sizeof(header);
-               cmd = (u32 *) cmdbuf->buf;
+               cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
+               cmd1 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
+               cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2);
+               cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3);
 
-               if (header.header.cmd_type != R300_CMD_PACKET3 ||
-                   header.packet3.packet != R300_CMD_PACKET3_RAW ||
-                   cmd[0] != CP_PACKET3(RADEON_CP_INDX_BUFFER, 2)) {
+               if (header->header.cmd_type != R300_CMD_PACKET3 ||
+                   header->packet3.packet != R300_CMD_PACKET3_RAW ||
+                   *cmd != CP_PACKET3(RADEON_CP_INDX_BUFFER, 2)) {
                        DRM_ERROR("3D_DRAW_INDX_2: expect subsequent INDX_BUFFER.\n");
                        return -EINVAL;
                }
 
-               if ((cmd[1] & 0x8000ffff) != 0x80000810) {
-                       DRM_ERROR("Invalid indx_buffer reg address %08X\n", cmd[1]);
+               if ((*cmd1 & 0x8000ffff) != 0x80000810) {
+                       DRM_ERROR("Invalid indx_buffer reg address %08X\n",
+                                       *cmd1);
                        return -EINVAL;
                }
-               if (!radeon_check_offset(dev_priv, cmd[2])) {
-                       DRM_ERROR("Invalid indx_buffer offset is %08X\n", cmd[2]);
+               if (!radeon_check_offset(dev_priv, *cmd2)) {
+                       DRM_ERROR("Invalid indx_buffer offset is %08X\n",
+                                       *cmd2);
                        return -EINVAL;
                }
-               if (cmd[3] != expected_count) {
+               if (*cmd3 != expected_count) {
                        DRM_ERROR("INDX_BUFFER: buffer size %i, expected %i\n",
-                               cmd[3], expected_count);
+                               *cmd3, expected_count);
                        return -EINVAL;
                }
 
                BEGIN_RING(4);
-               OUT_RING(cmd[0]);
-               OUT_RING_TABLE((int *)(cmdbuf->buf + 4), 3);
+               OUT_RING_DRM_BUFFER(cmdbuf->buffer, 4);
                ADVANCE_RING();
-
-               cmdbuf->buf += 4*4;
-               cmdbuf->bufsz -= 4*4;
        }
 
        return 0;
@@ -665,39 +651,39 @@ static __inline__ int r300_emit_draw_indx_2(drm_radeon_private_t *dev_priv,
 static __inline__ int r300_emit_raw_packet3(drm_radeon_private_t *dev_priv,
                                            drm_radeon_kcmd_buffer_t *cmdbuf)
 {
-       u32 header;
+       u32 *header;
        int count;
        RING_LOCALS;
 
-       if (4 > cmdbuf->bufsz)
+       if (4 > drm_buffer_unprocessed(cmdbuf->buffer))
                return -EINVAL;
 
        /* Fixme !! This simply emits a packet without much checking.
           We need to be smarter. */
 
        /* obtain first word - actual packet3 header */
-       header = *(u32 *) cmdbuf->buf;
+       header = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
 
        /* Is it packet 3 ? */
-       if ((header >> 30) != 0x3) {
-               DRM_ERROR("Not a packet3 header (0x%08x)\n", header);
+       if ((*header >> 30) != 0x3) {
+               DRM_ERROR("Not a packet3 header (0x%08x)\n", *header);
                return -EINVAL;
        }
 
-       count = (header >> 16) & 0x3fff;
+       count = (*header >> 16) & 0x3fff;
 
        /* Check again now that we know how much data to expect */
-       if ((count + 2) * 4 > cmdbuf->bufsz) {
+       if ((count + 2) * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) {
                DRM_ERROR
                    ("Expected packet3 of length %d but have only %d bytes left\n",
-                    (count + 2) * 4, cmdbuf->bufsz);
+                    (count + 2) * 4, drm_buffer_unprocessed(cmdbuf->buffer));
                return -EINVAL;
        }
 
        /* Is it a packet type we know about ? */
-       switch (header & 0xff00) {
+       switch (*header & 0xff00) {
        case RADEON_3D_LOAD_VBPNTR:     /* load vertex array pointers */
-               return r300_emit_3d_load_vbpntr(dev_priv, cmdbuf, header);
+               return r300_emit_3d_load_vbpntr(dev_priv, cmdbuf, *header);
 
        case RADEON_CNTL_BITBLT_MULTI:
                return r300_emit_bitblt_multi(dev_priv, cmdbuf);
@@ -723,18 +709,14 @@ static __inline__ int r300_emit_raw_packet3(drm_radeon_private_t *dev_priv,
                /* these packets are safe */
                break;
        default:
-               DRM_ERROR("Unknown packet3 header (0x%08x)\n", header);
+               DRM_ERROR("Unknown packet3 header (0x%08x)\n", *header);
                return -EINVAL;
        }
 
        BEGIN_RING(count + 2);
-       OUT_RING(header);
-       OUT_RING_TABLE((int *)(cmdbuf->buf + 4), count + 1);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, count + 2);
        ADVANCE_RING();
 
-       cmdbuf->buf += (count + 2) * 4;
-       cmdbuf->bufsz -= (count + 2) * 4;
-
        return 0;
 }
 
@@ -748,8 +730,7 @@ static __inline__ int r300_emit_packet3(drm_radeon_private_t *dev_priv,
 {
        int n;
        int ret;
-       char *orig_buf = cmdbuf->buf;
-       int orig_bufsz = cmdbuf->bufsz;
+       int orig_iter = cmdbuf->buffer->iterator;
 
        /* This is a do-while-loop so that we run the interior at least once,
         * even if cmdbuf->nbox is 0. Compare r300_emit_cliprects for rationale.
@@ -761,8 +742,7 @@ static __inline__ int r300_emit_packet3(drm_radeon_private_t *dev_priv,
                        if (ret)
                                return ret;
 
-                       cmdbuf->buf = orig_buf;
-                       cmdbuf->bufsz = orig_bufsz;
+                       cmdbuf->buffer->iterator = orig_iter;
                }
 
                switch (header.packet3.packet) {
@@ -785,9 +765,9 @@ static __inline__ int r300_emit_packet3(drm_radeon_private_t *dev_priv,
                        break;
 
                default:
-                       DRM_ERROR("bad packet3 type %i at %p\n",
+                       DRM_ERROR("bad packet3 type %i at byte %d\n",
                                  header.packet3.packet,
-                                 cmdbuf->buf - sizeof(header));
+                                 cmdbuf->buffer->iterator - (int)sizeof(header));
                        return -EINVAL;
                }
 
@@ -923,12 +903,13 @@ static int r300_scratch(drm_radeon_private_t *dev_priv,
                        drm_r300_cmd_header_t header)
 {
        u32 *ref_age_base;
-       u32 i, buf_idx, h_pending;
-       u64 ptr_addr;
+       u32 i, *buf_idx, h_pending;
+       u64 *ptr_addr;
+       u64 stack_ptr_addr;
        RING_LOCALS;
 
-       if (cmdbuf->bufsz <
-           (sizeof(u64) + header.scratch.n_bufs * sizeof(buf_idx))) {
+       if (drm_buffer_unprocessed(cmdbuf->buffer) <
+           (sizeof(u64) + header.scratch.n_bufs * sizeof(*buf_idx))) {
                return -EINVAL;
        }
 
@@ -938,36 +919,35 @@ static int r300_scratch(drm_radeon_private_t *dev_priv,
 
        dev_priv->scratch_ages[header.scratch.reg]++;
 
-       ptr_addr = get_unaligned((u64 *)cmdbuf->buf);
-       ref_age_base = (u32 *)(unsigned long)ptr_addr;
-
-       cmdbuf->buf += sizeof(u64);
-       cmdbuf->bufsz -= sizeof(u64);
+       ptr_addr = drm_buffer_read_object(cmdbuf->buffer,
+                       sizeof(stack_ptr_addr), &stack_ptr_addr);
+       ref_age_base = (u32 *)(unsigned long)*ptr_addr;
 
        for (i=0; i < header.scratch.n_bufs; i++) {
-               buf_idx = *(u32 *)cmdbuf->buf;
-               buf_idx *= 2; /* 8 bytes per buf */
+               buf_idx = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
+               *buf_idx *= 2; /* 8 bytes per buf */
 
-               if (DRM_COPY_TO_USER(ref_age_base + buf_idx, &dev_priv->scratch_ages[header.scratch.reg], sizeof(u32))) {
+               if (DRM_COPY_TO_USER(ref_age_base + *buf_idx,
+                               &dev_priv->scratch_ages[header.scratch.reg],
+                               sizeof(u32)))
                        return -EINVAL;
-               }
 
-               if (DRM_COPY_FROM_USER(&h_pending, ref_age_base + buf_idx + 1, sizeof(u32))) {
+               if (DRM_COPY_FROM_USER(&h_pending,
+                               ref_age_base + *buf_idx + 1,
+                               sizeof(u32)))
                        return -EINVAL;
-               }
 
-               if (h_pending == 0) {
+               if (h_pending == 0)
                        return -EINVAL;
-               }
 
                h_pending--;
 
-               if (DRM_COPY_TO_USER(ref_age_base + buf_idx + 1, &h_pending, sizeof(u32))) {
+               if (DRM_COPY_TO_USER(ref_age_base + *buf_idx + 1,
+                                       &h_pending,
+                                       sizeof(u32)))
                        return -EINVAL;
-               }
 
-               cmdbuf->buf += sizeof(buf_idx);
-               cmdbuf->bufsz -= sizeof(buf_idx);
+               drm_buffer_advance(cmdbuf->buffer, sizeof(*buf_idx));
        }
 
        BEGIN_RING(2);
@@ -1009,19 +989,16 @@ static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv,
        DRM_DEBUG("r500fp %d %d type: %d\n", sz, addr, type);
        if (!sz)
                return 0;
-       if (sz * stride * 4 > cmdbuf->bufsz)
+       if (sz * stride * 4 > drm_buffer_unprocessed(cmdbuf->buffer))
                return -EINVAL;
 
        BEGIN_RING(3 + sz * stride);
        OUT_RING_REG(R500_GA_US_VECTOR_INDEX, addr);
        OUT_RING(CP_PACKET0_TABLE(R500_GA_US_VECTOR_DATA, sz * stride - 1));
-       OUT_RING_TABLE((int *)cmdbuf->buf, sz * stride);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz * stride);
 
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * stride * 4;
-       cmdbuf->bufsz -= sz * stride * 4;
-
        return 0;
 }
 
@@ -1053,19 +1030,18 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
                        goto cleanup;
        }
 
-       while (cmdbuf->bufsz >= sizeof(drm_r300_cmd_header_t)) {
+       while (drm_buffer_unprocessed(cmdbuf->buffer)
+                       >= sizeof(drm_r300_cmd_header_t)) {
                int idx;
-               drm_r300_cmd_header_t header;
-
-               header.u = *(unsigned int *)cmdbuf->buf;
+               drm_r300_cmd_header_t *header, stack_header;
 
-               cmdbuf->buf += sizeof(header);
-               cmdbuf->bufsz -= sizeof(header);
+               header = drm_buffer_read_object(cmdbuf->buffer,
+                               sizeof(stack_header), &stack_header);
 
-               switch (header.header.cmd_type) {
+               switch (header->header.cmd_type) {
                case R300_CMD_PACKET0:
                        DRM_DEBUG("R300_CMD_PACKET0\n");
-                       ret = r300_emit_packet0(dev_priv, cmdbuf, header);
+                       ret = r300_emit_packet0(dev_priv, cmdbuf, *header);
                        if (ret) {
                                DRM_ERROR("r300_emit_packet0 failed\n");
                                goto cleanup;
@@ -1074,7 +1050,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
 
                case R300_CMD_VPU:
                        DRM_DEBUG("R300_CMD_VPU\n");
-                       ret = r300_emit_vpu(dev_priv, cmdbuf, header);
+                       ret = r300_emit_vpu(dev_priv, cmdbuf, *header);
                        if (ret) {
                                DRM_ERROR("r300_emit_vpu failed\n");
                                goto cleanup;
@@ -1083,7 +1059,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
 
                case R300_CMD_PACKET3:
                        DRM_DEBUG("R300_CMD_PACKET3\n");
-                       ret = r300_emit_packet3(dev_priv, cmdbuf, header);
+                       ret = r300_emit_packet3(dev_priv, cmdbuf, *header);
                        if (ret) {
                                DRM_ERROR("r300_emit_packet3 failed\n");
                                goto cleanup;
@@ -1117,8 +1093,8 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
                                int i;
                                RING_LOCALS;
 
-                               BEGIN_RING(header.delay.count);
-                               for (i = 0; i < header.delay.count; i++)
+                               BEGIN_RING(header->delay.count);
+                               for (i = 0; i < header->delay.count; i++)
                                        OUT_RING(RADEON_CP_PACKET2);
                                ADVANCE_RING();
                        }
@@ -1126,7 +1102,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
 
                case R300_CMD_DMA_DISCARD:
                        DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n");
-                       idx = header.dma.buf_idx;
+                       idx = header->dma.buf_idx;
                        if (idx < 0 || idx >= dma->buf_count) {
                                DRM_ERROR("buffer index %d (of %d max)\n",
                                          idx, dma->buf_count - 1);
@@ -1149,12 +1125,12 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
 
                case R300_CMD_WAIT:
                        DRM_DEBUG("R300_CMD_WAIT\n");
-                       r300_cmd_wait(dev_priv, header);
+                       r300_cmd_wait(dev_priv, *header);
                        break;
 
                case R300_CMD_SCRATCH:
                        DRM_DEBUG("R300_CMD_SCRATCH\n");
-                       ret = r300_scratch(dev_priv, cmdbuf, header);
+                       ret = r300_scratch(dev_priv, cmdbuf, *header);
                        if (ret) {
                                DRM_ERROR("r300_scratch failed\n");
                                goto cleanup;
@@ -1168,16 +1144,16 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
                                goto cleanup;
                        }
                        DRM_DEBUG("R300_CMD_R500FP\n");
-                       ret = r300_emit_r500fp(dev_priv, cmdbuf, header);
+                       ret = r300_emit_r500fp(dev_priv, cmdbuf, *header);
                        if (ret) {
                                DRM_ERROR("r300_emit_r500fp failed\n");
                                goto cleanup;
                        }
                        break;
                default:
-                       DRM_ERROR("bad cmd_type %i at %p\n",
-                                 header.header.cmd_type,
-                                 cmdbuf->buf - sizeof(header));
+                       DRM_ERROR("bad cmd_type %i at byte %d\n",
+                                 header->header.cmd_type,
+                                 cmdbuf->buffer->iterator - (int)sizeof(*header));
                        ret = -EINVAL;
                        goto cleanup;
                }
index 1735a2b695806af1c41ed9bd649f5d4a05b4d935..1a0d5362cd79982787f4f611affdf18aa22ddd93 100644 (file)
 #       define R300_TXO_ENDIAN_HALFDW_SWAP       (3 << 0)
 #       define R300_TXO_MACRO_TILE               (1 << 2)
 #       define R300_TXO_MICRO_TILE               (1 << 3)
+#       define R300_TXO_MICRO_TILE_SQUARE        (2 << 3)
 #       define R300_TXO_OFFSET_MASK              0xffffffe0
 #       define R300_TXO_OFFSET_SHIFT             5
        /* END: Guess from R200 */
 #       define R300_COLORPITCH_MASK              0x00001FF8 /* GUESS */
 #       define R300_COLOR_TILE_ENABLE            (1 << 16) /* GUESS */
 #       define R300_COLOR_MICROTILE_ENABLE       (1 << 17) /* GUESS */
+#       define R300_COLOR_MICROTILE_SQUARE_ENABLE (2 << 17)
 #       define R300_COLOR_ENDIAN_NO_SWAP         (0 << 18) /* GUESS */
 #       define R300_COLOR_ENDIAN_WORD_SWAP       (1 << 18) /* GUESS */
 #       define R300_COLOR_ENDIAN_DWORD_SWAP      (2 << 18) /* GUESS */
index d9373246c97f523513ee4a844ea3cb957b497944..c7593b8f58eeaf61d41c8860fc0f74306071564e 100644 (file)
@@ -40,28 +40,6 @@ static void r420_set_reg_safe(struct radeon_device *rdev)
        rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(r420_reg_safe_bm);
 }
 
-int r420_mc_init(struct radeon_device *rdev)
-{
-       int r;
-
-       /* Setup GPU memory space */
-       rdev->mc.vram_location = 0xFFFFFFFFUL;
-       rdev->mc.gtt_location = 0xFFFFFFFFUL;
-       if (rdev->flags & RADEON_IS_AGP) {
-               r = radeon_agp_init(rdev);
-               if (r) {
-                       radeon_agp_disable(rdev);
-               } else {
-                       rdev->mc.gtt_location = rdev->mc.agp_base;
-               }
-       }
-       r = radeon_mc_setup(rdev);
-       if (r) {
-               return r;
-       }
-       return 0;
-}
-
 void r420_pipes_init(struct radeon_device *rdev)
 {
        unsigned tmp;
@@ -69,7 +47,8 @@ void r420_pipes_init(struct radeon_device *rdev)
        unsigned num_pipes;
 
        /* GA_ENHANCE workaround TCL deadlock issue */
-       WREG32(0x4274, (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3));
+       WREG32(R300_GA_ENHANCE, R300_GA_DEADLOCK_CNTL | R300_GA_FASTSYNC_CNTL |
+              (1 << 2) | (1 << 3));
        /* add idle wait as per freedesktop.org bug 24041 */
        if (r100_gui_wait_for_idle(rdev)) {
                printk(KERN_WARNING "Failed to wait GUI idle while "
@@ -97,17 +76,17 @@ void r420_pipes_init(struct radeon_device *rdev)
                tmp = (7 << 1);
                break;
        }
-       WREG32(0x42C8, (1 << num_pipes) - 1);
+       WREG32(R500_SU_REG_DEST, (1 << num_pipes) - 1);
        /* Sub pixel 1/12 so we can have 4K rendering according to doc */
-       tmp |= (1 << 4) | (1 << 0);
-       WREG32(0x4018, tmp);
+       tmp |= R300_TILE_SIZE_16 | R300_ENABLE_TILING;
+       WREG32(R300_GB_TILE_CONFIG, tmp);
        if (r100_gui_wait_for_idle(rdev)) {
                printk(KERN_WARNING "Failed to wait GUI idle while "
                       "programming pipes. Bad things might happen.\n");
        }
 
-       tmp = RREG32(0x170C);
-       WREG32(0x170C, tmp | (1 << 31));
+       tmp = RREG32(R300_DST_PIPE_CONFIG);
+       WREG32(R300_DST_PIPE_CONFIG, tmp | R300_PIPE_AUTO_CONFIG);
 
        WREG32(R300_RB2D_DSTCACHE_MODE,
               RREG32(R300_RB2D_DSTCACHE_MODE) |
@@ -348,13 +327,15 @@ int r420_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       r300_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = r420_mc_init(rdev);
-       if (r) {
-               return r;
+       /* initialize AGP */
+       if (rdev->flags & RADEON_IS_AGP) {
+               r = radeon_agp_init(rdev);
+               if (r) {
+                       radeon_agp_disable(rdev);
+               }
        }
+       /* initialize memory controller */
+       r300_mc_init(rdev);
        r420_debugfs(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
index 74ad89bdf2b5f0bb6660a12e2607c4b982b458d3..0cf2ad2a558530634169fb2d3a73c497b7e9e777 100644 (file)
 #define AVIVO_DVOA_BIT_DEPTH_CONTROL                   0x7988
 
 #define AVIVO_DC_GPIO_HPD_A                 0x7e94
-
-#define AVIVO_GPIO_0                        0x7e30
-#define AVIVO_GPIO_1                        0x7e40
-#define AVIVO_GPIO_2                        0x7e50
-#define AVIVO_GPIO_3                        0x7e60
-
 #define AVIVO_DC_GPIO_HPD_Y                 0x7e9c
 
-#define AVIVO_I2C_STATUS                                       0x7d30
-#      define AVIVO_I2C_STATUS_DONE                            (1 << 0)
-#      define AVIVO_I2C_STATUS_NACK                            (1 << 1)
-#      define AVIVO_I2C_STATUS_HALT                            (1 << 2)
-#      define AVIVO_I2C_STATUS_GO                              (1 << 3)
-#      define AVIVO_I2C_STATUS_MASK                            0x7
-/* If radeon_mm_i2c is to be believed, this is HALT, NACK, and maybe
- * DONE? */
-#      define AVIVO_I2C_STATUS_CMD_RESET                       0x7
-#      define AVIVO_I2C_STATUS_CMD_WAIT                        (1 << 3)
-#define AVIVO_I2C_STOP                                         0x7d34
-#define AVIVO_I2C_START_CNTL                           0x7d38
-#      define AVIVO_I2C_START                                          (1 << 8)
-#      define AVIVO_I2C_CONNECTOR0                                     (0 << 16)
-#      define AVIVO_I2C_CONNECTOR1                                     (1 << 16)
-#define R520_I2C_START (1<<0)
-#define R520_I2C_STOP (1<<1)
-#define R520_I2C_RX (1<<2)
-#define R520_I2C_EN (1<<8)
-#define R520_I2C_DDC1 (0<<16)
-#define R520_I2C_DDC2 (1<<16)
-#define R520_I2C_DDC3 (2<<16)
-#define R520_I2C_DDC_MASK (3<<16)
-#define AVIVO_I2C_CONTROL2                                     0x7d3c
-#      define AVIVO_I2C_7D3C_SIZE_SHIFT                        8
-#      define AVIVO_I2C_7D3C_SIZE_MASK                         (0xf << 8)
-#define AVIVO_I2C_CONTROL3                                             0x7d40
-/* Reading is done 4 bytes at a time: read the bottom 8 bits from
- * 7d44, four times in a row.
- * Writing is a little more complex.  First write DATA with
- * 0xnnnnnnzz, then 0xnnnnnnyy, where nnnnnn is some non-deterministic
- * magic number, zz is, I think, the slave address, and yy is the byte
- * you want to write. */
-#define AVIVO_I2C_DATA                                         0x7d44
-#define R520_I2C_ADDR_COUNT_MASK (0x7)
-#define R520_I2C_DATA_COUNT_SHIFT (8)
-#define R520_I2C_DATA_COUNT_MASK (0xF00)
-#define AVIVO_I2C_CNTL                                         0x7d50
-#      define AVIVO_I2C_EN                                                     (1 << 0)
-#      define AVIVO_I2C_RESET                                          (1 << 8)
+#define AVIVO_DC_I2C_STATUS1                           0x7d30
+#      define AVIVO_DC_I2C_DONE                        (1 << 0)
+#      define AVIVO_DC_I2C_NACK                        (1 << 1)
+#      define AVIVO_DC_I2C_HALT                        (1 << 2)
+#      define AVIVO_DC_I2C_GO                          (1 << 3)
+#define AVIVO_DC_I2C_RESET                             0x7d34
+#      define AVIVO_DC_I2C_SOFT_RESET                  (1 << 0)
+#      define AVIVO_DC_I2C_ABORT                       (1 << 8)
+#define AVIVO_DC_I2C_CONTROL1                          0x7d38
+#      define AVIVO_DC_I2C_START                       (1 << 0)
+#      define AVIVO_DC_I2C_STOP                        (1 << 1)
+#      define AVIVO_DC_I2C_RECEIVE                     (1 << 2)
+#      define AVIVO_DC_I2C_EN                          (1 << 8)
+#      define AVIVO_DC_I2C_PIN_SELECT(x)               ((x) << 16)
+#      define AVIVO_SEL_DDC1                           0
+#      define AVIVO_SEL_DDC2                           1
+#      define AVIVO_SEL_DDC3                           2
+#define AVIVO_DC_I2C_CONTROL2                          0x7d3c
+#      define AVIVO_DC_I2C_ADDR_COUNT(x)               ((x) << 0)
+#      define AVIVO_DC_I2C_DATA_COUNT(x)               ((x) << 8)
+#define AVIVO_DC_I2C_CONTROL3                          0x7d40
+#      define AVIVO_DC_I2C_DATA_DRIVE_EN               (1 << 0)
+#      define AVIVO_DC_I2C_DATA_DRIVE_SEL              (1 << 1)
+#      define AVIVO_DC_I2C_CLK_DRIVE_EN                (1 << 7)
+#      define AVIVO_DC_I2C_RD_INTRA_BYTE_DELAY(x)      ((x) << 8)
+#      define AVIVO_DC_I2C_WR_INTRA_BYTE_DELAY(x)      ((x) << 16)
+#      define AVIVO_DC_I2C_TIME_LIMIT(x)               ((x) << 24)
+#define AVIVO_DC_I2C_DATA                              0x7d44
+#define AVIVO_DC_I2C_INTERRUPT_CONTROL                         0x7d48
+#      define AVIVO_DC_I2C_INTERRUPT_STATUS            (1 << 0)
+#      define AVIVO_DC_I2C_INTERRUPT_AK                (1 << 8)
+#      define AVIVO_DC_I2C_INTERRUPT_ENABLE            (1 << 16)
+#define AVIVO_DC_I2C_ARBITRATION                       0x7d50
+#      define AVIVO_DC_I2C_SW_WANTS_TO_USE_I2C         (1 << 0)
+#      define AVIVO_DC_I2C_SW_CAN_USE_I2C              (1 << 1)
+#      define AVIVO_DC_I2C_SW_DONE_USING_I2C           (1 << 8)
+#      define AVIVO_DC_I2C_HW_NEEDS_I2C                (1 << 9)
+#      define AVIVO_DC_I2C_ABORT_HDCP_I2C              (1 << 16)
+#      define AVIVO_DC_I2C_HW_USING_I2C                (1 << 17)
+
+#define AVIVO_DC_GPIO_DDC1_MASK                        0x7e40
+#define AVIVO_DC_GPIO_DDC1_A                           0x7e44
+#define AVIVO_DC_GPIO_DDC1_EN                          0x7e48
+#define AVIVO_DC_GPIO_DDC1_Y                           0x7e4c
+
+#define AVIVO_DC_GPIO_DDC2_MASK                        0x7e50
+#define AVIVO_DC_GPIO_DDC2_A                           0x7e54
+#define AVIVO_DC_GPIO_DDC2_EN                          0x7e58
+#define AVIVO_DC_GPIO_DDC2_Y                           0x7e5c
+
+#define AVIVO_DC_GPIO_DDC3_MASK                        0x7e60
+#define AVIVO_DC_GPIO_DDC3_A                           0x7e64
+#define AVIVO_DC_GPIO_DDC3_EN                          0x7e68
+#define AVIVO_DC_GPIO_DDC3_Y                           0x7e6c
 
 #define AVIVO_DISP_INTERRUPT_STATUS                             0x7edc
 #       define AVIVO_D1_VBLANK_INTERRUPT                        (1 << 4)
index ddf5731eba0d8f294294a7a3bc4674ce2040a52e..2b8a5dd1351672bbacbf260d33441910514ff7a9 100644 (file)
@@ -119,13 +119,15 @@ static void r520_vram_get_type(struct radeon_device *rdev)
                rdev->mc.vram_width *= 2;
 }
 
-void r520_vram_info(struct radeon_device *rdev)
+void r520_mc_init(struct radeon_device *rdev)
 {
        fixed20_12 a;
 
        r520_vram_get_type(rdev);
-
        r100_vram_init_sizes(rdev);
+       radeon_vram_location(rdev, &rdev->mc, 0);
+       if (!(rdev->flags & RADEON_IS_AGP))
+               radeon_gtt_location(rdev, &rdev->mc);
        /* FIXME: we should enforce default clock in case GPU is not in
         * default setup
         */
@@ -267,12 +269,15 @@ int r520_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       r520_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = r420_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize AGP */
+       if (rdev->flags & RADEON_IS_AGP) {
+               r = radeon_agp_init(rdev);
+               if (r) {
+                       radeon_agp_disable(rdev);
+               }
+       }
+       /* initialize memory controller */
+       r520_mc_init(rdev);
        rv515_debugfs(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
index 2ffcf5a03551e94b5fdbc32d3dca1eeae8c9ba67..c52290197292a1a958d05dcd3896587055542b8b 100644 (file)
@@ -353,23 +353,14 @@ void r600_hpd_fini(struct radeon_device *rdev)
 /*
  * R600 PCIE GART
  */
-int r600_gart_clear_page(struct radeon_device *rdev, int i)
-{
-       void __iomem *ptr = (void *)rdev->gart.table.vram.ptr;
-       u64 pte;
-
-       if (i < 0 || i > rdev->gart.num_gpu_pages)
-               return -EINVAL;
-       pte = 0;
-       writeq(pte, ((void __iomem *)ptr) + (i * 8));
-       return 0;
-}
-
 void r600_pcie_gart_tlb_flush(struct radeon_device *rdev)
 {
        unsigned i;
        u32 tmp;
 
+       /* flush hdp cache so updates hit vram */
+       WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
+
        WREG32(VM_CONTEXT0_INVALIDATION_LOW_ADDR, rdev->mc.gtt_start >> 12);
        WREG32(VM_CONTEXT0_INVALIDATION_HIGH_ADDR, (rdev->mc.gtt_end - 1) >> 12);
        WREG32(VM_CONTEXT0_REQUEST_RESPONSE, REQUEST_TYPE(1));
@@ -416,6 +407,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
        r = radeon_gart_table_vram_pin(rdev);
        if (r)
                return r;
+       radeon_gart_restore(rdev);
 
        /* Setup L2 cache */
        WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
@@ -619,6 +611,68 @@ static void r600_mc_program(struct radeon_device *rdev)
        rv515_vga_render_disable(rdev);
 }
 
+/**
+ * r600_vram_gtt_location - try to find VRAM & GTT location
+ * @rdev: radeon device structure holding all necessary informations
+ * @mc: memory controller structure holding memory informations
+ *
+ * Function will place try to place VRAM at same place as in CPU (PCI)
+ * address space as some GPU seems to have issue when we reprogram at
+ * different address space.
+ *
+ * If there is not enough space to fit the unvisible VRAM after the
+ * aperture then we limit the VRAM size to the aperture.
+ *
+ * If we are using AGP then place VRAM adjacent to AGP aperture are we need
+ * them to be in one from GPU point of view so that we can program GPU to
+ * catch access outside them (weird GPU policy see ??).
+ *
+ * This function will never fails, worst case are limiting VRAM or GTT.
+ *
+ * Note: GTT start, end, size should be initialized before calling this
+ * function on AGP platform.
+ */
+void r600_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc)
+{
+       u64 size_bf, size_af;
+
+       if (mc->mc_vram_size > 0xE0000000) {
+               /* leave room for at least 512M GTT */
+               dev_warn(rdev->dev, "limiting VRAM\n");
+               mc->real_vram_size = 0xE0000000;
+               mc->mc_vram_size = 0xE0000000;
+       }
+       if (rdev->flags & RADEON_IS_AGP) {
+               size_bf = mc->gtt_start;
+               size_af = 0xFFFFFFFF - mc->gtt_end + 1;
+               if (size_bf > size_af) {
+                       if (mc->mc_vram_size > size_bf) {
+                               dev_warn(rdev->dev, "limiting VRAM\n");
+                               mc->real_vram_size = size_bf;
+                               mc->mc_vram_size = size_bf;
+                       }
+                       mc->vram_start = mc->gtt_start - mc->mc_vram_size;
+               } else {
+                       if (mc->mc_vram_size > size_af) {
+                               dev_warn(rdev->dev, "limiting VRAM\n");
+                               mc->real_vram_size = size_af;
+                               mc->mc_vram_size = size_af;
+                       }
+                       mc->vram_start = mc->gtt_end;
+               }
+               mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;
+               dev_info(rdev->dev, "VRAM: %lluM 0x%08llX - 0x%08llX (%lluM used)\n",
+                               mc->mc_vram_size >> 20, mc->vram_start,
+                               mc->vram_end, mc->real_vram_size >> 20);
+       } else {
+               u64 base = 0;
+               if (rdev->flags & RADEON_IS_IGP)
+                       base = (RREG32(MC_VM_FB_LOCATION) & 0xFFFF) << 24;
+               radeon_vram_location(rdev, &rdev->mc, base);
+               radeon_gtt_location(rdev, mc);
+       }
+}
+
 int r600_mc_init(struct radeon_device *rdev)
 {
        fixed20_12 a;
@@ -658,75 +712,21 @@ int r600_mc_init(struct radeon_device *rdev)
        /* Setup GPU memory space */
        rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE);
        rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE);
-
-       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
+       rdev->mc.visible_vram_size = rdev->mc.aper_size;
+       /* FIXME remove this once we support unmappable VRAM */
+       if (rdev->mc.mc_vram_size > rdev->mc.aper_size) {
                rdev->mc.mc_vram_size = rdev->mc.aper_size;
-
-       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
                rdev->mc.real_vram_size = rdev->mc.aper_size;
-
-       if (rdev->flags & RADEON_IS_AGP) {
-               /* gtt_size is setup by radeon_agp_init */
-               rdev->mc.gtt_location = rdev->mc.agp_base;
-               tmp = 0xFFFFFFFFUL - rdev->mc.agp_base - rdev->mc.gtt_size;
-               /* Try to put vram before or after AGP because we
-                * we want SYSTEM_APERTURE to cover both VRAM and
-                * AGP so that GPU can catch out of VRAM/AGP access
-                */
-               if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
-                       /* Enough place before */
-                       rdev->mc.vram_location = rdev->mc.gtt_location -
-                                                       rdev->mc.mc_vram_size;
-               } else if (tmp > rdev->mc.mc_vram_size) {
-                       /* Enough place after */
-                       rdev->mc.vram_location = rdev->mc.gtt_location +
-                                                       rdev->mc.gtt_size;
-               } else {
-                       /* Try to setup VRAM then AGP might not
-                        * not work on some card
-                        */
-                       rdev->mc.vram_location = 0x00000000UL;
-                       rdev->mc.gtt_location = rdev->mc.mc_vram_size;
-               }
-       } else {
-               rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
-               rdev->mc.vram_location = (RREG32(MC_VM_FB_LOCATION) &
-                                                       0xFFFF) << 24;
-               tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size;
-               if ((0xFFFFFFFFUL - tmp) >= rdev->mc.gtt_size) {
-                       /* Enough place after vram */
-                       rdev->mc.gtt_location = tmp;
-               } else if (rdev->mc.vram_location >= rdev->mc.gtt_size) {
-                       /* Enough place before vram */
-                       rdev->mc.gtt_location = 0;
-               } else {
-                       /* Not enough place after or before shrink
-                        * gart size
-                        */
-                       if (rdev->mc.vram_location > (0xFFFFFFFFUL - tmp)) {
-                               rdev->mc.gtt_location = 0;
-                               rdev->mc.gtt_size = rdev->mc.vram_location;
-                       } else {
-                               rdev->mc.gtt_location = tmp;
-                               rdev->mc.gtt_size = 0xFFFFFFFFUL - tmp;
-                       }
-               }
-               rdev->mc.gtt_location = rdev->mc.mc_vram_size;
        }
-       rdev->mc.vram_start = rdev->mc.vram_location;
-       rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1;
-       rdev->mc.gtt_start = rdev->mc.gtt_location;
-       rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
+       r600_vram_gtt_location(rdev, &rdev->mc);
        /* FIXME: we should enforce default clock in case GPU is not in
         * default setup
         */
        a.full = rfixed_const(100);
        rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk);
        rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a);
-
        if (rdev->flags & RADEON_IS_IGP)
                rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
-
        return 0;
 }
 
@@ -981,6 +981,9 @@ void r600_gpu_init(struct radeon_device *rdev)
 {
        u32 tiling_config;
        u32 ramcfg;
+       u32 backend_map;
+       u32 cc_rb_backend_disable;
+       u32 cc_gc_shader_pipe_config;
        u32 tmp;
        int i, j;
        u32 sq_config;
@@ -1090,8 +1093,11 @@ void r600_gpu_init(struct radeon_device *rdev)
        default:
                break;
        }
+       rdev->config.r600.tiling_npipes = rdev->config.r600.max_tile_pipes;
+       rdev->config.r600.tiling_nbanks = 4 << ((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT);
        tiling_config |= BANK_TILING((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT);
        tiling_config |= GROUP_SIZE(0);
+       rdev->config.r600.tiling_group_size = 256;
        tmp = (ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
        if (tmp > 3) {
                tiling_config |= ROW_TILING(3);
@@ -1101,24 +1107,33 @@ void r600_gpu_init(struct radeon_device *rdev)
                tiling_config |= SAMPLE_SPLIT(tmp);
        }
        tiling_config |= BANK_SWAPS(1);
-       tmp = r600_get_tile_pipe_to_backend_map(rdev->config.r600.max_tile_pipes,
-                                               rdev->config.r600.max_backends,
-                                               (0xff << rdev->config.r600.max_backends) & 0xff);
-       tiling_config |= BACKEND_MAP(tmp);
+
+       cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000;
+       cc_rb_backend_disable |=
+               BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << rdev->config.r600.max_backends) & R6XX_MAX_BACKENDS_MASK);
+
+       cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00;
+       cc_gc_shader_pipe_config |=
+               INACTIVE_QD_PIPES((R6XX_MAX_PIPES_MASK << rdev->config.r600.max_pipes) & R6XX_MAX_PIPES_MASK);
+       cc_gc_shader_pipe_config |=
+               INACTIVE_SIMDS((R6XX_MAX_SIMDS_MASK << rdev->config.r600.max_simds) & R6XX_MAX_SIMDS_MASK);
+
+       backend_map = r600_get_tile_pipe_to_backend_map(rdev->config.r600.max_tile_pipes,
+                                                       (R6XX_MAX_BACKENDS -
+                                                        r600_count_pipe_bits((cc_rb_backend_disable &
+                                                                              R6XX_MAX_BACKENDS_MASK) >> 16)),
+                                                       (cc_rb_backend_disable >> 16));
+
+       tiling_config |= BACKEND_MAP(backend_map);
        WREG32(GB_TILING_CONFIG, tiling_config);
        WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff);
        WREG32(HDP_TILING_CONFIG, tiling_config & 0xffff);
 
-       tmp = BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << rdev->config.r600.max_backends) & R6XX_MAX_BACKENDS_MASK);
-       WREG32(CC_RB_BACKEND_DISABLE, tmp);
-
        /* Setup pipes */
-       tmp = INACTIVE_QD_PIPES((R6XX_MAX_PIPES_MASK << rdev->config.r600.max_pipes) & R6XX_MAX_PIPES_MASK);
-       tmp |= INACTIVE_SIMDS((R6XX_MAX_SIMDS_MASK << rdev->config.r600.max_simds) & R6XX_MAX_SIMDS_MASK);
-       WREG32(CC_GC_SHADER_PIPE_CONFIG, tmp);
-       WREG32(GC_USER_SHADER_PIPE_CONFIG, tmp);
+       WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable);
+       WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config);
 
-       tmp = R6XX_MAX_BACKENDS - r600_count_pipe_bits(tmp & INACTIVE_QD_PIPES_MASK);
+       tmp = R6XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8);
        WREG32(VGT_OUT_DEALLOC_CNTL, (tmp * 4) & DEALLOC_DIST_MASK);
        WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((tmp * 4) - 2) & VTX_REUSE_DEPTH_MASK);
 
@@ -1783,12 +1798,17 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
                          struct radeon_fence *fence)
 {
        /* Also consider EVENT_WRITE_EOP.  it handles the interrupts + timestamps + events */
+
+       radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
+       radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
+       /* wait for 3D idle clean */
+       radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+       radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
+       radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
        /* Emit fence sequence & fire IRQ */
        radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
        radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
        radeon_ring_write(rdev, fence->seq);
-       radeon_ring_write(rdev, PACKET0(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0));
-       radeon_ring_write(rdev, 1);
        /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
        radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
        radeon_ring_write(rdev, RB_INT_STAT);
@@ -2745,6 +2765,7 @@ restart_ih:
                        case 0: /* D1 vblank */
                                if (disp_int & LB_D1_VBLANK_INTERRUPT) {
                                        drm_handle_vblank(rdev->ddev, 0);
+                                       wake_up(&rdev->irq.vblank_queue);
                                        disp_int &= ~LB_D1_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D1 vblank\n");
                                }
@@ -2765,6 +2786,7 @@ restart_ih:
                        case 0: /* D2 vblank */
                                if (disp_int & LB_D2_VBLANK_INTERRUPT) {
                                        drm_handle_vblank(rdev->ddev, 1);
+                                       wake_up(&rdev->irq.vblank_queue);
                                        disp_int &= ~LB_D2_VBLANK_INTERRUPT;
                                        DRM_DEBUG("IH: D2 vblank\n");
                                }
index 0dcb6904c4ff930bbaaa4bc81e2da930ecdc6af8..db928016d034cc1e5fce5eba0718cc1a45dc3c0a 100644 (file)
@@ -35,7 +35,7 @@
  */
 static int r600_audio_chipset_supported(struct radeon_device *rdev)
 {
-       return (rdev->family >= CHIP_R600 && rdev->family < CHIP_RV710)
+       return rdev->family >= CHIP_R600
                || rdev->family == CHIP_RS600
                || rdev->family == CHIP_RS690
                || rdev->family == CHIP_RS740;
@@ -146,16 +146,24 @@ static void r600_audio_update_hdmi(unsigned long param)
                jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
 }
 
+/*
+ * turn on/off audio engine
+ */
+static void r600_audio_engine_enable(struct radeon_device *rdev, bool enable)
+{
+       DRM_INFO("%s audio support", enable ? "Enabling" : "Disabling");
+       WREG32_P(R600_AUDIO_ENABLE, enable ? 0x81000000 : 0x0, ~0x81000000);
+}
+
 /*
  * initialize the audio vars and register the update timer
  */
 int r600_audio_init(struct radeon_device *rdev)
 {
-       if (!r600_audio_chipset_supported(rdev))
+       if (!radeon_audio || !r600_audio_chipset_supported(rdev))
                return 0;
 
-       DRM_INFO("%s audio support", radeon_audio ? "Enabling" : "Disabling");
-       WREG32_P(R600_AUDIO_ENABLE, radeon_audio ? 0x81000000 : 0x0, ~0x81000000);
+       r600_audio_engine_enable(rdev, true);
 
        rdev->audio_channels = -1;
        rdev->audio_rate = -1;
@@ -258,9 +266,10 @@ void r600_audio_set_clock(struct drm_encoder *encoder, int clock)
  */
 void r600_audio_fini(struct radeon_device *rdev)
 {
-       if (!r600_audio_chipset_supported(rdev))
+       if (!radeon_audio || !r600_audio_chipset_supported(rdev))
                return;
 
        del_timer(&rdev->audio_timer);
-       WREG32_P(R600_AUDIO_ENABLE, 0x0, ~0x81000000);
+
+       r600_audio_engine_enable(rdev, false);
 }
index 5ea432347589a77c81181719294da4c6da5bba5e..f4fb88ece2bbc0221e9a967518a94745fb545be6 100644 (file)
@@ -49,7 +49,7 @@ set_render_target(drm_radeon_private_t *dev_priv, int format, int w, int h, u64
        RING_LOCALS;
        DRM_DEBUG("\n");
 
-       h = (h + 7) & ~7;
+       h = ALIGN(h, 8);
        if (h < 8)
                h = 8;
 
index 446b765ac72a5338678a15124bdab152693dedc9..f6c6c77db7e004278d549f0c60f9a36cbd627094 100644 (file)
@@ -25,7 +25,7 @@ set_render_target(struct radeon_device *rdev, int format,
        u32 cb_color_info;
        int pitch, slice;
 
-       h = (h + 7) & ~7;
+       h = ALIGN(h, 8);
        if (h < 8)
                h = 8;
 
@@ -396,15 +396,13 @@ set_default_state(struct radeon_device *rdev)
                                    NUM_ES_STACK_ENTRIES(num_es_stack_entries));
 
        /* emit an IB pointing at default state */
-       dwords = (rdev->r600_blit.state_len + 0xf) & ~0xf;
+       dwords = ALIGN(rdev->r600_blit.state_len, 0x10);
        gpu_addr = rdev->r600_blit.shader_gpu_addr + rdev->r600_blit.state_offset;
        radeon_ring_write(rdev, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
        radeon_ring_write(rdev, gpu_addr & 0xFFFFFFFC);
        radeon_ring_write(rdev, upper_32_bits(gpu_addr) & 0xFF);
        radeon_ring_write(rdev, dwords);
 
-       radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
-       radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
        /* SQ config */
        radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 6));
        radeon_ring_write(rdev, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
@@ -578,9 +576,9 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
        ring_size = num_loops * dwords_per_loop;
        /* set default  + shaders */
        ring_size += 40; /* shaders + def state */
-       ring_size += 7; /* fence emit for VB IB */
+       ring_size += 10; /* fence emit for VB IB */
        ring_size += 5; /* done copy */
-       ring_size += 7; /* fence emit for done copy */
+       ring_size += 10; /* fence emit for done copy */
        r = radeon_ring_lock(rdev, ring_size);
        if (r)
                return r;
@@ -594,13 +592,6 @@ void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
 {
        int r;
 
-       radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
-       radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
-       /* wait for 3D idle clean */
-       radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
-       radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
-       radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
-
        if (rdev->r600_blit.vb_ib)
                r600_vb_ib_put(rdev);
 
index d745e815c2e81193bfe9e44c129efc028018985f..a112c59f9d824988a8f0a399380c4041eace903d 100644 (file)
@@ -9,11 +9,6 @@ const u32 r6xx_default_state[] =
        0xc0012800,
        0x80000000,
        0x80000000,
-       0xc0004600,
-       0x00000016,
-       0xc0016800,
-       0x00000010,
-       0x00028000,
        0xc0016800,
        0x00000010,
        0x00008000,
@@ -531,11 +526,6 @@ const u32 r7xx_default_state[] =
        0xc0012800,
        0x80000000,
        0x80000000,
-       0xc0004600,
-       0x00000016,
-       0xc0016800,
-       0x00000010,
-       0x00028000,
        0xc0016800,
        0x00000010,
        0x00008000,
index 75bcf35a09312437f0df3c77ffb51e2548218aa8..40416c068d9f431491f85acba93462839700d22b 100644 (file)
@@ -734,8 +734,8 @@ static void r600_gfx_init(struct drm_device *dev,
        u32 hdp_host_path_cntl;
        u32 backend_map;
        u32 gb_tiling_config = 0;
-       u32 cc_rb_backend_disable = 0;
-       u32 cc_gc_shader_pipe_config = 0;
+       u32 cc_rb_backend_disable;
+       u32 cc_gc_shader_pipe_config;
        u32 ramcfg;
 
        /* setup chip specs */
@@ -857,29 +857,44 @@ static void r600_gfx_init(struct drm_device *dev,
 
        gb_tiling_config |= R600_BANK_SWAPS(1);
 
-       backend_map = r600_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
-                                                       dev_priv->r600_max_backends,
-                                                       (0xff << dev_priv->r600_max_backends) & 0xff);
-       gb_tiling_config |= R600_BACKEND_MAP(backend_map);
+       cc_rb_backend_disable = RADEON_READ(R600_CC_RB_BACKEND_DISABLE) & 0x00ff0000;
+       cc_rb_backend_disable |=
+               R600_BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << dev_priv->r600_max_backends) & R6XX_MAX_BACKENDS_MASK);
 
-       cc_gc_shader_pipe_config =
+       cc_gc_shader_pipe_config = RADEON_READ(R600_CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00;
+       cc_gc_shader_pipe_config |=
                R600_INACTIVE_QD_PIPES((R6XX_MAX_PIPES_MASK << dev_priv->r600_max_pipes) & R6XX_MAX_PIPES_MASK);
        cc_gc_shader_pipe_config |=
                R600_INACTIVE_SIMDS((R6XX_MAX_SIMDS_MASK << dev_priv->r600_max_simds) & R6XX_MAX_SIMDS_MASK);
 
-       cc_rb_backend_disable =
-               R600_BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << dev_priv->r600_max_backends) & R6XX_MAX_BACKENDS_MASK);
+       backend_map = r600_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
+                                                       (R6XX_MAX_BACKENDS -
+                                                        r600_count_pipe_bits((cc_rb_backend_disable &
+                                                                              R6XX_MAX_BACKENDS_MASK) >> 16)),
+                                                       (cc_rb_backend_disable >> 16));
+       gb_tiling_config |= R600_BACKEND_MAP(backend_map);
 
        RADEON_WRITE(R600_GB_TILING_CONFIG,      gb_tiling_config);
        RADEON_WRITE(R600_DCP_TILING_CONFIG,    (gb_tiling_config & 0xffff));
        RADEON_WRITE(R600_HDP_TILING_CONFIG,    (gb_tiling_config & 0xffff));
+       if (gb_tiling_config & 0xc0) {
+               dev_priv->r600_group_size = 512;
+       } else {
+               dev_priv->r600_group_size = 256;
+       }
+       dev_priv->r600_npipes = 1 << ((gb_tiling_config >> 1) & 0x7);
+       if (gb_tiling_config & 0x30) {
+               dev_priv->r600_nbanks = 8;
+       } else {
+               dev_priv->r600_nbanks = 4;
+       }
 
        RADEON_WRITE(R600_CC_RB_BACKEND_DISABLE,      cc_rb_backend_disable);
        RADEON_WRITE(R600_CC_GC_SHADER_PIPE_CONFIG,   cc_gc_shader_pipe_config);
        RADEON_WRITE(R600_GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config);
 
        num_qd_pipes =
-               R6XX_MAX_BACKENDS - r600_count_pipe_bits(cc_gc_shader_pipe_config & R600_INACTIVE_QD_PIPES_MASK);
+               R6XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & R600_INACTIVE_QD_PIPES_MASK) >> 8);
        RADEON_WRITE(R600_VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & R600_DEALLOC_DIST_MASK);
        RADEON_WRITE(R600_VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & R600_VTX_REUSE_DEPTH_MASK);
 
@@ -1151,7 +1166,8 @@ static void r600_gfx_init(struct drm_device *dev,
 
 }
 
-static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
+static u32 r700_get_tile_pipe_to_backend_map(drm_radeon_private_t *dev_priv,
+                                            u32 num_tile_pipes,
                                             u32 num_backends,
                                             u32 backend_disable_mask)
 {
@@ -1162,6 +1178,7 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
        u32 swizzle_pipe[R7XX_MAX_PIPES];
        u32 cur_backend;
        u32 i;
+       bool force_no_swizzle;
 
        if (num_tile_pipes > R7XX_MAX_PIPES)
                num_tile_pipes = R7XX_MAX_PIPES;
@@ -1191,6 +1208,18 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
        if (enabled_backends_count != num_backends)
                num_backends = enabled_backends_count;
 
+       switch (dev_priv->flags & RADEON_FAMILY_MASK) {
+       case CHIP_RV770:
+       case CHIP_RV730:
+               force_no_swizzle = false;
+               break;
+       case CHIP_RV710:
+       case CHIP_RV740:
+       default:
+               force_no_swizzle = true;
+               break;
+       }
+
        memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R7XX_MAX_PIPES);
        switch (num_tile_pipes) {
        case 1:
@@ -1201,49 +1230,100 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
                swizzle_pipe[1] = 1;
                break;
        case 3:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 1;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 1;
+               }
                break;
        case 4:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 3;
-               swizzle_pipe[3] = 1;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 3;
+                       swizzle_pipe[3] = 1;
+               }
                break;
        case 5:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 1;
-               swizzle_pipe[4] = 3;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 1;
+                       swizzle_pipe[4] = 3;
+               }
                break;
        case 6:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 5;
-               swizzle_pipe[4] = 3;
-               swizzle_pipe[5] = 1;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+                       swizzle_pipe[5] = 5;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 5;
+                       swizzle_pipe[4] = 3;
+                       swizzle_pipe[5] = 1;
+               }
                break;
        case 7:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 6;
-               swizzle_pipe[4] = 3;
-               swizzle_pipe[5] = 1;
-               swizzle_pipe[6] = 5;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+                       swizzle_pipe[5] = 5;
+                       swizzle_pipe[6] = 6;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 6;
+                       swizzle_pipe[4] = 3;
+                       swizzle_pipe[5] = 1;
+                       swizzle_pipe[6] = 5;
+               }
                break;
        case 8:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 6;
-               swizzle_pipe[4] = 3;
-               swizzle_pipe[5] = 1;
-               swizzle_pipe[6] = 7;
-               swizzle_pipe[7] = 5;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+                       swizzle_pipe[5] = 5;
+                       swizzle_pipe[6] = 6;
+                       swizzle_pipe[7] = 7;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 6;
+                       swizzle_pipe[4] = 3;
+                       swizzle_pipe[5] = 1;
+                       swizzle_pipe[6] = 7;
+                       swizzle_pipe[7] = 5;
+               }
                break;
        }
 
@@ -1264,8 +1344,10 @@ static void r700_gfx_init(struct drm_device *dev,
                          drm_radeon_private_t *dev_priv)
 {
        int i, j, num_qd_pipes;
+       u32 ta_aux_cntl;
        u32 sx_debug_1;
        u32 smx_dc_ctl0;
+       u32 db_debug3;
        u32 num_gs_verts_per_thread;
        u32 vgt_gs_per_es;
        u32 gs_prim_buffer_depth = 0;
@@ -1276,8 +1358,8 @@ static void r700_gfx_init(struct drm_device *dev,
        u32 sq_dyn_gpr_size_simd_ab_0;
        u32 backend_map;
        u32 gb_tiling_config = 0;
-       u32 cc_rb_backend_disable = 0;
-       u32 cc_gc_shader_pipe_config = 0;
+       u32 cc_rb_backend_disable;
+       u32 cc_gc_shader_pipe_config;
        u32 mc_arb_ramcfg;
        u32 db_debug4;
 
@@ -1428,38 +1510,51 @@ static void r700_gfx_init(struct drm_device *dev,
 
        gb_tiling_config |= R600_BANK_SWAPS(1);
 
-       if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740)
-               backend_map = 0x28;
-       else
-               backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
-                                                               dev_priv->r600_max_backends,
-                                                               (0xff << dev_priv->r600_max_backends) & 0xff);
-       gb_tiling_config |= R600_BACKEND_MAP(backend_map);
+       cc_rb_backend_disable = RADEON_READ(R600_CC_RB_BACKEND_DISABLE) & 0x00ff0000;
+       cc_rb_backend_disable |=
+               R600_BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << dev_priv->r600_max_backends) & R7XX_MAX_BACKENDS_MASK);
 
-       cc_gc_shader_pipe_config =
+       cc_gc_shader_pipe_config = RADEON_READ(R600_CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00;
+       cc_gc_shader_pipe_config |=
                R600_INACTIVE_QD_PIPES((R7XX_MAX_PIPES_MASK << dev_priv->r600_max_pipes) & R7XX_MAX_PIPES_MASK);
        cc_gc_shader_pipe_config |=
                R600_INACTIVE_SIMDS((R7XX_MAX_SIMDS_MASK << dev_priv->r600_max_simds) & R7XX_MAX_SIMDS_MASK);
 
-       cc_rb_backend_disable =
-               R600_BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << dev_priv->r600_max_backends) & R7XX_MAX_BACKENDS_MASK);
+       if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740)
+               backend_map = 0x28;
+       else
+               backend_map = r700_get_tile_pipe_to_backend_map(dev_priv,
+                                                               dev_priv->r600_max_tile_pipes,
+                                                               (R7XX_MAX_BACKENDS -
+                                                                r600_count_pipe_bits((cc_rb_backend_disable &
+                                                                                      R7XX_MAX_BACKENDS_MASK) >> 16)),
+                                                               (cc_rb_backend_disable >> 16));
+       gb_tiling_config |= R600_BACKEND_MAP(backend_map);
 
        RADEON_WRITE(R600_GB_TILING_CONFIG,      gb_tiling_config);
        RADEON_WRITE(R600_DCP_TILING_CONFIG,    (gb_tiling_config & 0xffff));
        RADEON_WRITE(R600_HDP_TILING_CONFIG,    (gb_tiling_config & 0xffff));
+       if (gb_tiling_config & 0xc0) {
+               dev_priv->r600_group_size = 512;
+       } else {
+               dev_priv->r600_group_size = 256;
+       }
+       dev_priv->r600_npipes = 1 << ((gb_tiling_config >> 1) & 0x7);
+       if (gb_tiling_config & 0x30) {
+               dev_priv->r600_nbanks = 8;
+       } else {
+               dev_priv->r600_nbanks = 4;
+       }
 
        RADEON_WRITE(R600_CC_RB_BACKEND_DISABLE,      cc_rb_backend_disable);
        RADEON_WRITE(R600_CC_GC_SHADER_PIPE_CONFIG,   cc_gc_shader_pipe_config);
-       RADEON_WRITE(R600_GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config);
 
        RADEON_WRITE(R700_CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable);
        RADEON_WRITE(R700_CGTS_SYS_TCC_DISABLE, 0);
        RADEON_WRITE(R700_CGTS_TCC_DISABLE, 0);
-       RADEON_WRITE(R700_CGTS_USER_SYS_TCC_DISABLE, 0);
-       RADEON_WRITE(R700_CGTS_USER_TCC_DISABLE, 0);
 
        num_qd_pipes =
-               R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_gc_shader_pipe_config & R600_INACTIVE_QD_PIPES_MASK);
+               R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & R600_INACTIVE_QD_PIPES_MASK) >> 8);
        RADEON_WRITE(R600_VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & R600_DEALLOC_DIST_MASK);
        RADEON_WRITE(R600_VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & R600_VTX_REUSE_DEPTH_MASK);
 
@@ -1469,10 +1564,8 @@ static void r700_gfx_init(struct drm_device *dev,
 
        RADEON_WRITE(R600_CP_MEQ_THRESHOLDS, R700_STQ_SPLIT(0x30));
 
-       RADEON_WRITE(R600_TA_CNTL_AUX, (R600_DISABLE_CUBE_ANISO |
-                                       R600_SYNC_GRADIENT |
-                                       R600_SYNC_WALKER |
-                                       R600_SYNC_ALIGNER));
+       ta_aux_cntl = RADEON_READ(R600_TA_CNTL_AUX);
+       RADEON_WRITE(R600_TA_CNTL_AUX, ta_aux_cntl | R600_DISABLE_CUBE_ANISO);
 
        sx_debug_1 = RADEON_READ(R700_SX_DEBUG_1);
        sx_debug_1 |= R700_ENABLE_NEW_SMX_ADDRESS;
@@ -1483,14 +1576,28 @@ static void r700_gfx_init(struct drm_device *dev,
        smx_dc_ctl0 |= R700_CACHE_DEPTH((dev_priv->r700_sx_num_of_sets * 64) - 1);
        RADEON_WRITE(R600_SMX_DC_CTL0, smx_dc_ctl0);
 
-       RADEON_WRITE(R700_SMX_EVENT_CTL, (R700_ES_FLUSH_CTL(4) |
-                                         R700_GS_FLUSH_CTL(4) |
-                                         R700_ACK_FLUSH_CTL(3) |
-                                         R700_SYNC_FLUSH_CTL));
+       if ((dev_priv->flags & RADEON_FAMILY_MASK) != CHIP_RV740)
+               RADEON_WRITE(R700_SMX_EVENT_CTL, (R700_ES_FLUSH_CTL(4) |
+                                                 R700_GS_FLUSH_CTL(4) |
+                                                 R700_ACK_FLUSH_CTL(3) |
+                                                 R700_SYNC_FLUSH_CTL));
 
-       if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV770)
-               RADEON_WRITE(R700_DB_DEBUG3, R700_DB_CLK_OFF_DELAY(0x1f));
-       else {
+       db_debug3 = RADEON_READ(R700_DB_DEBUG3);
+       db_debug3 &= ~R700_DB_CLK_OFF_DELAY(0x1f);
+       switch (dev_priv->flags & RADEON_FAMILY_MASK) {
+       case CHIP_RV770:
+       case CHIP_RV740:
+               db_debug3 |= R700_DB_CLK_OFF_DELAY(0x1f);
+               break;
+       case CHIP_RV710:
+       case CHIP_RV730:
+       default:
+               db_debug3 |= R700_DB_CLK_OFF_DELAY(2);
+               break;
+       }
+       RADEON_WRITE(R700_DB_DEBUG3, db_debug3);
+
+       if ((dev_priv->flags & RADEON_FAMILY_MASK) != CHIP_RV770) {
                db_debug4 = RADEON_READ(RV700_DB_DEBUG4);
                db_debug4 |= RV700_DISABLE_TILE_COVERED_FOR_PS_ITER;
                RADEON_WRITE(RV700_DB_DEBUG4, db_debug4);
@@ -1519,10 +1626,10 @@ static void r700_gfx_init(struct drm_device *dev,
                            R600_ALU_UPDATE_FIFO_HIWATER(0x8));
        switch (dev_priv->flags & RADEON_FAMILY_MASK) {
        case CHIP_RV770:
-               sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x1);
-               break;
        case CHIP_RV730:
        case CHIP_RV710:
+               sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x1);
+               break;
        case CHIP_RV740:
        default:
                sq_ms_fifo_sizes |= R600_FETCH_FIFO_HIWATER(0x4);
@@ -2529,3 +2636,12 @@ out:
        mutex_unlock(&dev_priv->cs_mutex);
        return r;
 }
+
+void r600_cs_legacy_get_tiling_conf(struct drm_device *dev, u32 *npipes, u32 *nbanks, u32 *group_size)
+{
+       struct drm_radeon_private *dev_priv = dev->dev_private;
+
+       *npipes = dev_priv->r600_npipes;
+       *nbanks = dev_priv->r600_nbanks;
+       *group_size = dev_priv->r600_group_size;
+}
index e4c45ec16507fce420811d8b498218c00084034a..cd2c63bce50172f6136dbeb64abfe69254e2d122 100644 (file)
@@ -28,6 +28,7 @@
 #include "drmP.h"
 #include "radeon.h"
 #include "r600d.h"
+#include "r600_reg_safe.h"
 
 static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p,
                                        struct radeon_cs_reloc **cs_reloc);
@@ -35,11 +36,313 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
                                        struct radeon_cs_reloc **cs_reloc);
 typedef int (*next_reloc_t)(struct radeon_cs_parser*, struct radeon_cs_reloc**);
 static next_reloc_t r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_mm;
+extern void r600_cs_legacy_get_tiling_conf(struct drm_device *dev, u32 *npipes, u32 *nbanks, u32 *group_size);
+
 
 struct r600_cs_track {
-       u32     cb_color0_base_last;
+       /* configuration we miror so that we use same code btw kms/ums */
+       u32                     group_size;
+       u32                     nbanks;
+       u32                     npipes;
+       /* value we track */
+       u32                     nsamples;
+       u32                     cb_color_base_last[8];
+       struct radeon_bo        *cb_color_bo[8];
+       u32                     cb_color_bo_offset[8];
+       struct radeon_bo        *cb_color_frag_bo[8];
+       struct radeon_bo        *cb_color_tile_bo[8];
+       u32                     cb_color_info[8];
+       u32                     cb_color_size_idx[8];
+       u32                     cb_target_mask;
+       u32                     cb_shader_mask;
+       u32                     cb_color_size[8];
+       u32                     vgt_strmout_en;
+       u32                     vgt_strmout_buffer_en;
+       u32                     db_depth_control;
+       u32                     db_depth_info;
+       u32                     db_depth_size_idx;
+       u32                     db_depth_view;
+       u32                     db_depth_size;
+       u32                     db_offset;
+       struct radeon_bo        *db_bo;
 };
 
+static inline int r600_bpe_from_format(u32 *bpe, u32 format)
+{
+       switch (format) {
+       case V_038004_COLOR_8:
+       case V_038004_COLOR_4_4:
+       case V_038004_COLOR_3_3_2:
+       case V_038004_FMT_1:
+               *bpe = 1;
+               break;
+       case V_038004_COLOR_16:
+       case V_038004_COLOR_16_FLOAT:
+       case V_038004_COLOR_8_8:
+       case V_038004_COLOR_5_6_5:
+       case V_038004_COLOR_6_5_5:
+       case V_038004_COLOR_1_5_5_5:
+       case V_038004_COLOR_4_4_4_4:
+       case V_038004_COLOR_5_5_5_1:
+               *bpe = 2;
+               break;
+       case V_038004_FMT_8_8_8:
+               *bpe = 3;
+               break;
+       case V_038004_COLOR_32:
+       case V_038004_COLOR_32_FLOAT:
+       case V_038004_COLOR_16_16:
+       case V_038004_COLOR_16_16_FLOAT:
+       case V_038004_COLOR_8_24:
+       case V_038004_COLOR_8_24_FLOAT:
+       case V_038004_COLOR_24_8:
+       case V_038004_COLOR_24_8_FLOAT:
+       case V_038004_COLOR_10_11_11:
+       case V_038004_COLOR_10_11_11_FLOAT:
+       case V_038004_COLOR_11_11_10:
+       case V_038004_COLOR_11_11_10_FLOAT:
+       case V_038004_COLOR_2_10_10_10:
+       case V_038004_COLOR_8_8_8_8:
+       case V_038004_COLOR_10_10_10_2:
+       case V_038004_FMT_5_9_9_9_SHAREDEXP:
+       case V_038004_FMT_32_AS_8:
+       case V_038004_FMT_32_AS_8_8:
+               *bpe = 4;
+               break;
+       case V_038004_COLOR_X24_8_32_FLOAT:
+       case V_038004_COLOR_32_32:
+       case V_038004_COLOR_32_32_FLOAT:
+       case V_038004_COLOR_16_16_16_16:
+       case V_038004_COLOR_16_16_16_16_FLOAT:
+               *bpe = 8;
+               break;
+       case V_038004_FMT_16_16_16:
+       case V_038004_FMT_16_16_16_FLOAT:
+               *bpe = 6;
+               break;
+       case V_038004_FMT_32_32_32:
+       case V_038004_FMT_32_32_32_FLOAT:
+               *bpe = 12;
+               break;
+       case V_038004_COLOR_32_32_32_32:
+       case V_038004_COLOR_32_32_32_32_FLOAT:
+               *bpe = 16;
+               break;
+       case V_038004_FMT_GB_GR:
+       case V_038004_FMT_BG_RG:
+       case V_038004_COLOR_INVALID:
+               *bpe = 16;
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void r600_cs_track_init(struct r600_cs_track *track)
+{
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               track->cb_color_base_last[i] = 0;
+               track->cb_color_size[i] = 0;
+               track->cb_color_size_idx[i] = 0;
+               track->cb_color_info[i] = 0;
+               track->cb_color_bo[i] = NULL;
+               track->cb_color_bo_offset[i] = 0xFFFFFFFF;
+       }
+       track->cb_target_mask = 0xFFFFFFFF;
+       track->cb_shader_mask = 0xFFFFFFFF;
+       track->db_bo = NULL;
+       /* assume the biggest format and that htile is enabled */
+       track->db_depth_info = 7 | (1 << 25);
+       track->db_depth_view = 0xFFFFC000;
+       track->db_depth_size = 0xFFFFFFFF;
+       track->db_depth_size_idx = 0;
+       track->db_depth_control = 0xFFFFFFFF;
+}
+
+static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
+{
+       struct r600_cs_track *track = p->track;
+       u32 bpe = 0, pitch, slice_tile_max, size, tmp, height;
+       volatile u32 *ib = p->ib->ptr;
+
+       if (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
+               dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
+               return -EINVAL;
+       }
+       size = radeon_bo_size(track->cb_color_bo[i]);
+       if (r600_bpe_from_format(&bpe, G_0280A0_FORMAT(track->cb_color_info[i]))) {
+               dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n",
+                        __func__, __LINE__, G_0280A0_FORMAT(track->cb_color_info[i]),
+                       i, track->cb_color_info[i]);
+               return -EINVAL;
+       }
+       pitch = (G_028060_PITCH_TILE_MAX(track->cb_color_size[i]) + 1) << 3;
+       slice_tile_max = G_028060_SLICE_TILE_MAX(track->cb_color_size[i]) + 1;
+       if (!pitch) {
+               dev_warn(p->dev, "%s:%d cb pitch (%d) for %d invalid (0x%08X)\n",
+                       __func__, __LINE__, pitch, i, track->cb_color_size[i]);
+               return -EINVAL;
+       }
+       height = size / (pitch * bpe);
+       if (height > 8192)
+               height = 8192;
+       switch (G_0280A0_ARRAY_MODE(track->cb_color_info[i])) {
+       case V_0280A0_ARRAY_LINEAR_GENERAL:
+       case V_0280A0_ARRAY_LINEAR_ALIGNED:
+               if (pitch & 0x3f) {
+                       dev_warn(p->dev, "%s:%d cb pitch (%d x %d = %d) invalid\n",
+                               __func__, __LINE__, pitch, bpe, pitch * bpe);
+                       return -EINVAL;
+               }
+               if ((pitch * bpe) & (track->group_size - 1)) {
+                       dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n",
+                               __func__, __LINE__, pitch);
+                       return -EINVAL;
+               }
+               break;
+       case V_0280A0_ARRAY_1D_TILED_THIN1:
+               if ((pitch * 8 * bpe * track->nsamples) & (track->group_size - 1)) {
+                       dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n",
+                               __func__, __LINE__, pitch);
+                       return -EINVAL;
+               }
+               height &= ~0x7;
+               if (!height)
+                       height = 8;
+               break;
+       case V_0280A0_ARRAY_2D_TILED_THIN1:
+               if (pitch & ((8 * track->nbanks) - 1)) {
+                       dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n",
+                               __func__, __LINE__, pitch);
+                       return -EINVAL;
+               }
+               tmp = pitch * 8 * bpe * track->nsamples;
+               tmp = tmp / track->nbanks;
+               if (tmp & (track->group_size - 1)) {
+                       dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n",
+                               __func__, __LINE__, pitch);
+                       return -EINVAL;
+               }
+               height &= ~((16 * track->npipes) - 1);
+               if (!height)
+                       height = 16 * track->npipes;
+               break;
+       default:
+               dev_warn(p->dev, "%s invalid tiling %d for %d (0x%08X)\n", __func__,
+                       G_0280A0_ARRAY_MODE(track->cb_color_info[i]), i,
+                       track->cb_color_info[i]);
+               return -EINVAL;
+       }
+       /* check offset */
+       tmp = height * pitch;
+       if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) {
+               dev_warn(p->dev, "%s offset[%d] %d to big\n", __func__, i, track->cb_color_bo_offset[i]);
+               return -EINVAL;
+       }
+       /* limit max tile */
+       tmp = (height * pitch) >> 6;
+       if (tmp < slice_tile_max)
+               slice_tile_max = tmp;
+       tmp = S_028060_PITCH_TILE_MAX((pitch >> 3) - 1) |
+               S_028060_SLICE_TILE_MAX(slice_tile_max - 1);
+       ib[track->cb_color_size_idx[i]] = tmp;
+       return 0;
+}
+
+static int r600_cs_track_check(struct radeon_cs_parser *p)
+{
+       struct r600_cs_track *track = p->track;
+       u32 tmp;
+       int r, i;
+       volatile u32 *ib = p->ib->ptr;
+
+       /* on legacy kernel we don't perform advanced check */
+       if (p->rdev == NULL)
+               return 0;
+       /* we don't support out buffer yet */
+       if (track->vgt_strmout_en || track->vgt_strmout_buffer_en) {
+               dev_warn(p->dev, "this kernel doesn't support SMX output buffer\n");
+               return -EINVAL;
+       }
+       /* check that we have a cb for each enabled target, we don't check
+        * shader_mask because it seems mesa isn't always setting it :(
+        */
+       tmp = track->cb_target_mask;
+       for (i = 0; i < 8; i++) {
+               if ((tmp >> (i * 4)) & 0xF) {
+                       /* at least one component is enabled */
+                       if (track->cb_color_bo[i] == NULL) {
+                               dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
+                                       __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
+                               return -EINVAL;
+                       }
+                       /* perform rewrite of CB_COLOR[0-7]_SIZE */
+                       r = r600_cs_track_validate_cb(p, i);
+                       if (r)
+                               return r;
+               }
+       }
+       /* Check depth buffer */
+       if (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
+               G_028800_Z_ENABLE(track->db_depth_control)) {
+               u32 nviews, bpe, ntiles;
+               if (track->db_bo == NULL) {
+                       dev_warn(p->dev, "z/stencil with no depth buffer\n");
+                       return -EINVAL;
+               }
+               if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) {
+                       dev_warn(p->dev, "this kernel doesn't support z/stencil htile\n");
+                       return -EINVAL;
+               }
+               switch (G_028010_FORMAT(track->db_depth_info)) {
+               case V_028010_DEPTH_16:
+                       bpe = 2;
+                       break;
+               case V_028010_DEPTH_X8_24:
+               case V_028010_DEPTH_8_24:
+               case V_028010_DEPTH_X8_24_FLOAT:
+               case V_028010_DEPTH_8_24_FLOAT:
+               case V_028010_DEPTH_32_FLOAT:
+                       bpe = 4;
+                       break;
+               case V_028010_DEPTH_X24_8_32_FLOAT:
+                       bpe = 8;
+                       break;
+               default:
+                       dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info));
+                       return -EINVAL;
+               }
+               if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) {
+                       if (!track->db_depth_size_idx) {
+                               dev_warn(p->dev, "z/stencil buffer size not set\n");
+                               return -EINVAL;
+                       }
+                       printk_once(KERN_WARNING "You have old & broken userspace please consider updating mesa\n");
+                       tmp = radeon_bo_size(track->db_bo) - track->db_offset;
+                       tmp = (tmp / bpe) >> 6;
+                       if (!tmp) {
+                               dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %ld)\n",
+                                               track->db_depth_size, bpe, track->db_offset,
+                                               radeon_bo_size(track->db_bo));
+                               return -EINVAL;
+                       }
+                       ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF);
+               } else {
+                       ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
+                       nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
+                       tmp = ntiles * bpe * 64 * nviews;
+                       if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
+                               dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %d -> %d have %ld)\n",
+                                               track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset,
+                                               radeon_bo_size(track->db_bo));
+                               return -EINVAL;
+                       }
+               }
+       }
+       return 0;
+}
+
 /**
  * r600_cs_packet_parse() - parse cp packet and point ib index to next packet
  * @parser:    parser structure holding parsing context.
@@ -359,6 +662,334 @@ static int r600_cs_parse_packet0(struct radeon_cs_parser *p,
        return 0;
 }
 
+/**
+ * r600_cs_check_reg() - check if register is authorized or not
+ * @parser: parser structure holding parsing context
+ * @reg: register we are testing
+ * @idx: index into the cs buffer
+ *
+ * This function will test against r600_reg_safe_bm and return 0
+ * if register is safe. If register is not flag as safe this function
+ * will test it against a list of register needind special handling.
+ */
+static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
+{
+       struct r600_cs_track *track = (struct r600_cs_track *)p->track;
+       struct radeon_cs_reloc *reloc;
+       u32 last_reg = ARRAY_SIZE(r600_reg_safe_bm);
+       u32 m, i, tmp, *ib;
+       int r;
+
+       i = (reg >> 7);
+       if (i > last_reg) {
+               dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
+               return -EINVAL;
+       }
+       m = 1 << ((reg >> 2) & 31);
+       if (!(r600_reg_safe_bm[i] & m))
+               return 0;
+       ib = p->ib->ptr;
+       switch (reg) {
+       /* force following reg to 0 in an attemp to disable out buffer
+        * which will need us to better understand how it works to perform
+        * security check on it (Jerome)
+        */
+       case R_0288A8_SQ_ESGS_RING_ITEMSIZE:
+       case R_008C44_SQ_ESGS_RING_SIZE:
+       case R_0288B0_SQ_ESTMP_RING_ITEMSIZE:
+       case R_008C54_SQ_ESTMP_RING_SIZE:
+       case R_0288C0_SQ_FBUF_RING_ITEMSIZE:
+       case R_008C74_SQ_FBUF_RING_SIZE:
+       case R_0288B4_SQ_GSTMP_RING_ITEMSIZE:
+       case R_008C5C_SQ_GSTMP_RING_SIZE:
+       case R_0288AC_SQ_GSVS_RING_ITEMSIZE:
+       case R_008C4C_SQ_GSVS_RING_SIZE:
+       case R_0288BC_SQ_PSTMP_RING_ITEMSIZE:
+       case R_008C6C_SQ_PSTMP_RING_SIZE:
+       case R_0288C4_SQ_REDUC_RING_ITEMSIZE:
+       case R_008C7C_SQ_REDUC_RING_SIZE:
+       case R_0288B8_SQ_VSTMP_RING_ITEMSIZE:
+       case R_008C64_SQ_VSTMP_RING_SIZE:
+       case R_0288C8_SQ_GS_VERT_ITEMSIZE:
+               /* get value to populate the IB don't remove */
+               tmp =radeon_get_ib_value(p, idx);
+               ib[idx] = 0;
+               break;
+       case R_028800_DB_DEPTH_CONTROL:
+               track->db_depth_control = radeon_get_ib_value(p, idx);
+               break;
+       case R_028010_DB_DEPTH_INFO:
+               track->db_depth_info = radeon_get_ib_value(p, idx);
+               break;
+       case R_028004_DB_DEPTH_VIEW:
+               track->db_depth_view = radeon_get_ib_value(p, idx);
+               break;
+       case R_028000_DB_DEPTH_SIZE:
+               track->db_depth_size = radeon_get_ib_value(p, idx);
+               track->db_depth_size_idx = idx;
+               break;
+       case R_028AB0_VGT_STRMOUT_EN:
+               track->vgt_strmout_en = radeon_get_ib_value(p, idx);
+               break;
+       case R_028B20_VGT_STRMOUT_BUFFER_EN:
+               track->vgt_strmout_buffer_en = radeon_get_ib_value(p, idx);
+               break;
+       case R_028238_CB_TARGET_MASK:
+               track->cb_target_mask = radeon_get_ib_value(p, idx);
+               break;
+       case R_02823C_CB_SHADER_MASK:
+               track->cb_shader_mask = radeon_get_ib_value(p, idx);
+               break;
+       case R_028C04_PA_SC_AA_CONFIG:
+               tmp = G_028C04_MSAA_NUM_SAMPLES(radeon_get_ib_value(p, idx));
+               track->nsamples = 1 << tmp;
+               break;
+       case R_0280A0_CB_COLOR0_INFO:
+       case R_0280A4_CB_COLOR1_INFO:
+       case R_0280A8_CB_COLOR2_INFO:
+       case R_0280AC_CB_COLOR3_INFO:
+       case R_0280B0_CB_COLOR4_INFO:
+       case R_0280B4_CB_COLOR5_INFO:
+       case R_0280B8_CB_COLOR6_INFO:
+       case R_0280BC_CB_COLOR7_INFO:
+               tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4;
+               track->cb_color_info[tmp] = radeon_get_ib_value(p, idx);
+               break;
+       case R_028060_CB_COLOR0_SIZE:
+       case R_028064_CB_COLOR1_SIZE:
+       case R_028068_CB_COLOR2_SIZE:
+       case R_02806C_CB_COLOR3_SIZE:
+       case R_028070_CB_COLOR4_SIZE:
+       case R_028074_CB_COLOR5_SIZE:
+       case R_028078_CB_COLOR6_SIZE:
+       case R_02807C_CB_COLOR7_SIZE:
+               tmp = (reg - R_028060_CB_COLOR0_SIZE) / 4;
+               track->cb_color_size[tmp] = radeon_get_ib_value(p, idx);
+               track->cb_color_size_idx[tmp] = idx;
+               break;
+               /* This register were added late, there is userspace
+                * which does provide relocation for those but set
+                * 0 offset. In order to avoid breaking old userspace
+                * we detect this and set address to point to last
+                * CB_COLOR0_BASE, note that if userspace doesn't set
+                * CB_COLOR0_BASE before this register we will report
+                * error. Old userspace always set CB_COLOR0_BASE
+                * before any of this.
+                */
+       case R_0280E0_CB_COLOR0_FRAG:
+       case R_0280E4_CB_COLOR1_FRAG:
+       case R_0280E8_CB_COLOR2_FRAG:
+       case R_0280EC_CB_COLOR3_FRAG:
+       case R_0280F0_CB_COLOR4_FRAG:
+       case R_0280F4_CB_COLOR5_FRAG:
+       case R_0280F8_CB_COLOR6_FRAG:
+       case R_0280FC_CB_COLOR7_FRAG:
+               tmp = (reg - R_0280E0_CB_COLOR0_FRAG) / 4;
+               if (!r600_cs_packet_next_is_pkt3_nop(p)) {
+                       if (!track->cb_color_base_last[tmp]) {
+                               dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
+                               return -EINVAL;
+                       }
+                       ib[idx] = track->cb_color_base_last[tmp];
+                       printk_once(KERN_WARNING "You have old & broken userspace "
+                                       "please consider updating mesa & xf86-video-ati\n");
+                       track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp];
+               } else {
+                       r = r600_cs_packet_next_reloc(p, &reloc);
+                       if (r) {
+                               dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
+                               return -EINVAL;
+                       }
+                       ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+                       track->cb_color_frag_bo[tmp] = reloc->robj;
+               }
+               break;
+       case R_0280C0_CB_COLOR0_TILE:
+       case R_0280C4_CB_COLOR1_TILE:
+       case R_0280C8_CB_COLOR2_TILE:
+       case R_0280CC_CB_COLOR3_TILE:
+       case R_0280D0_CB_COLOR4_TILE:
+       case R_0280D4_CB_COLOR5_TILE:
+       case R_0280D8_CB_COLOR6_TILE:
+       case R_0280DC_CB_COLOR7_TILE:
+               tmp = (reg - R_0280C0_CB_COLOR0_TILE) / 4;
+               if (!r600_cs_packet_next_is_pkt3_nop(p)) {
+                       if (!track->cb_color_base_last[tmp]) {
+                               dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
+                               return -EINVAL;
+                       }
+                       ib[idx] = track->cb_color_base_last[tmp];
+                       printk_once(KERN_WARNING "You have old & broken userspace "
+                                       "please consider updating mesa & xf86-video-ati\n");
+                       track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp];
+               } else {
+                       r = r600_cs_packet_next_reloc(p, &reloc);
+                       if (r) {
+                               dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
+                               return -EINVAL;
+                       }
+                       ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+                       track->cb_color_tile_bo[tmp] = reloc->robj;
+               }
+               break;
+       case CB_COLOR0_BASE:
+       case CB_COLOR1_BASE:
+       case CB_COLOR2_BASE:
+       case CB_COLOR3_BASE:
+       case CB_COLOR4_BASE:
+       case CB_COLOR5_BASE:
+       case CB_COLOR6_BASE:
+       case CB_COLOR7_BASE:
+               r = r600_cs_packet_next_reloc(p, &reloc);
+               if (r) {
+                       dev_warn(p->dev, "bad SET_CONTEXT_REG "
+                                       "0x%04X\n", reg);
+                       return -EINVAL;
+               }
+               tmp = (reg - CB_COLOR0_BASE) / 4;
+               track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
+               ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+               track->cb_color_base_last[tmp] = ib[idx];
+               track->cb_color_bo[tmp] = reloc->robj;
+               break;
+       case DB_DEPTH_BASE:
+               r = r600_cs_packet_next_reloc(p, &reloc);
+               if (r) {
+                       dev_warn(p->dev, "bad SET_CONTEXT_REG "
+                                       "0x%04X\n", reg);
+                       return -EINVAL;
+               }
+               track->db_offset = radeon_get_ib_value(p, idx);
+               ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+               track->db_bo = reloc->robj;
+               break;
+       case DB_HTILE_DATA_BASE:
+       case SQ_PGM_START_FS:
+       case SQ_PGM_START_ES:
+       case SQ_PGM_START_VS:
+       case SQ_PGM_START_GS:
+       case SQ_PGM_START_PS:
+               r = r600_cs_packet_next_reloc(p, &reloc);
+               if (r) {
+                       dev_warn(p->dev, "bad SET_CONTEXT_REG "
+                                       "0x%04X\n", reg);
+                       return -EINVAL;
+               }
+               ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+               break;
+       default:
+               dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static inline unsigned minify(unsigned size, unsigned levels)
+{
+       size = size >> levels;
+       if (size < 1)
+               size = 1;
+       return size;
+}
+
+static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels,
+                               unsigned w0, unsigned h0, unsigned d0, unsigned bpe,
+                               unsigned *l0_size, unsigned *mipmap_size)
+{
+       unsigned offset, i, level, face;
+       unsigned width, height, depth, rowstride, size;
+
+       w0 = minify(w0, 0);
+       h0 = minify(h0, 0);
+       d0 = minify(d0, 0);
+       for(i = 0, offset = 0, level = blevel; i < nlevels; i++, level++) {
+               width = minify(w0, i);
+               height = minify(h0, i);
+               depth = minify(d0, i);
+               for(face = 0; face < nfaces; face++) {
+                       rowstride = ((width * bpe) + 255) & ~255;
+                       size = height * rowstride * depth;
+                       offset += size;
+                       offset = (offset + 0x1f) & ~0x1f;
+               }
+       }
+       *l0_size = (((w0 * bpe) + 255) & ~255) * h0 * d0;
+       *mipmap_size = offset;
+       if (!blevel)
+               *mipmap_size -= *l0_size;
+       if (!nlevels)
+               *mipmap_size = *l0_size;
+}
+
+/**
+ * r600_check_texture_resource() - check if register is authorized or not
+ * @p: parser structure holding parsing context
+ * @idx: index into the cs buffer
+ * @texture: texture's bo structure
+ * @mipmap: mipmap's bo structure
+ *
+ * This function will check that the resource has valid field and that
+ * the texture and mipmap bo object are big enough to cover this resource.
+ */
+static inline int r600_check_texture_resource(struct radeon_cs_parser *p,  u32 idx,
+                                               struct radeon_bo *texture,
+                                               struct radeon_bo *mipmap)
+{
+       u32 nfaces, nlevels, blevel, w0, h0, d0, bpe = 0;
+       u32 word0, word1, l0_size, mipmap_size;
+
+       /* on legacy kernel we don't perform advanced check */
+       if (p->rdev == NULL)
+               return 0;
+       word0 = radeon_get_ib_value(p, idx + 0);
+       word1 = radeon_get_ib_value(p, idx + 1);
+       w0 = G_038000_TEX_WIDTH(word0) + 1;
+       h0 = G_038004_TEX_HEIGHT(word1) + 1;
+       d0 = G_038004_TEX_DEPTH(word1);
+       nfaces = 1;
+       switch (G_038000_DIM(word0)) {
+       case V_038000_SQ_TEX_DIM_1D:
+       case V_038000_SQ_TEX_DIM_2D:
+       case V_038000_SQ_TEX_DIM_3D:
+               break;
+       case V_038000_SQ_TEX_DIM_CUBEMAP:
+               nfaces = 6;
+               break;
+       case V_038000_SQ_TEX_DIM_1D_ARRAY:
+       case V_038000_SQ_TEX_DIM_2D_ARRAY:
+       case V_038000_SQ_TEX_DIM_2D_MSAA:
+       case V_038000_SQ_TEX_DIM_2D_ARRAY_MSAA:
+       default:
+               dev_warn(p->dev, "this kernel doesn't support %d texture dim\n", G_038000_DIM(word0));
+               return -EINVAL;
+       }
+       if (r600_bpe_from_format(&bpe,  G_038004_DATA_FORMAT(word1))) {
+               dev_warn(p->dev, "%s:%d texture invalid format %d\n",
+                        __func__, __LINE__, G_038004_DATA_FORMAT(word1));
+               return -EINVAL;
+       }
+       word0 = radeon_get_ib_value(p, idx + 4);
+       word1 = radeon_get_ib_value(p, idx + 5);
+       blevel = G_038010_BASE_LEVEL(word0);
+       nlevels = G_038014_LAST_LEVEL(word1);
+       r600_texture_size(nfaces, blevel, nlevels, w0, h0, d0, bpe, &l0_size, &mipmap_size);
+       /* using get ib will give us the offset into the texture bo */
+       word0 = radeon_get_ib_value(p, idx + 2);
+       if ((l0_size + word0) > radeon_bo_size(texture)) {
+               dev_warn(p->dev, "texture bo too small (%d %d %d %d -> %d have %ld)\n",
+                       w0, h0, bpe, word0, l0_size, radeon_bo_size(texture));
+               return -EINVAL;
+       }
+       /* using get ib will give us the offset into the mipmap bo */
+       word0 = radeon_get_ib_value(p, idx + 3);
+       if ((mipmap_size + word0) > radeon_bo_size(mipmap)) {
+               dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n",
+                       w0, h0, bpe, blevel, nlevels, word0, mipmap_size, radeon_bo_size(texture));
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int r600_packet3_check(struct radeon_cs_parser *p,
                                struct radeon_cs_packet *pkt)
 {
@@ -408,12 +1039,22 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                }
                ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
                ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+               r = r600_cs_track_check(p);
+               if (r) {
+                       dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
+                       return r;
+               }
                break;
        case PACKET3_DRAW_INDEX_AUTO:
                if (pkt->count != 1) {
                        DRM_ERROR("bad DRAW_INDEX_AUTO\n");
                        return -EINVAL;
                }
+               r = r600_cs_track_check(p);
+               if (r) {
+                       dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx);
+                       return r;
+               }
                break;
        case PACKET3_DRAW_INDEX_IMMD_BE:
        case PACKET3_DRAW_INDEX_IMMD:
@@ -421,6 +1062,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                        DRM_ERROR("bad DRAW_INDEX_IMMD\n");
                        return -EINVAL;
                }
+               r = r600_cs_track_check(p);
+               if (r) {
+                       dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
+                       return r;
+               }
                break;
        case PACKET3_WAIT_REG_MEM:
                if (pkt->count != 5) {
@@ -493,30 +1139,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                }
                for (i = 0; i < pkt->count; i++) {
                        reg = start_reg + (4 * i);
-                       switch (reg) {
-                       case SQ_ESGS_RING_BASE:
-                       case SQ_GSVS_RING_BASE:
-                       case SQ_ESTMP_RING_BASE:
-                       case SQ_GSTMP_RING_BASE:
-                       case SQ_VSTMP_RING_BASE:
-                       case SQ_PSTMP_RING_BASE:
-                       case SQ_FBUF_RING_BASE:
-                       case SQ_REDUC_RING_BASE:
-                       case SX_MEMORY_EXPORT_BASE:
-                               r = r600_cs_packet_next_reloc(p, &reloc);
-                               if (r) {
-                                       DRM_ERROR("bad SET_CONFIG_REG "
-                                                       "0x%04X\n", reg);
-                                       return -EINVAL;
-                               }
-                               ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
-                               break;
-                       case CP_COHER_BASE:
-                               /* use PACKET3_SURFACE_SYNC */
-                               return -EINVAL;
-                       default:
-                               break;
-                       }
+                       r = r600_cs_check_reg(p, reg, idx+1+i);
+                       if (r)
+                               return r;
                }
                break;
        case PACKET3_SET_CONTEXT_REG:
@@ -530,106 +1155,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                }
                for (i = 0; i < pkt->count; i++) {
                        reg = start_reg + (4 * i);
-                       switch (reg) {
-                       /* This register were added late, there is userspace
-                        * which does provide relocation for those but set
-                        * 0 offset. In order to avoid breaking old userspace
-                        * we detect this and set address to point to last
-                        * CB_COLOR0_BASE, note that if userspace doesn't set
-                        * CB_COLOR0_BASE before this register we will report
-                        * error. Old userspace always set CB_COLOR0_BASE
-                        * before any of this.
-                        */
-                       case R_0280E0_CB_COLOR0_FRAG:
-                       case R_0280E4_CB_COLOR1_FRAG:
-                       case R_0280E8_CB_COLOR2_FRAG:
-                       case R_0280EC_CB_COLOR3_FRAG:
-                       case R_0280F0_CB_COLOR4_FRAG:
-                       case R_0280F4_CB_COLOR5_FRAG:
-                       case R_0280F8_CB_COLOR6_FRAG:
-                       case R_0280FC_CB_COLOR7_FRAG:
-                       case R_0280C0_CB_COLOR0_TILE:
-                       case R_0280C4_CB_COLOR1_TILE:
-                       case R_0280C8_CB_COLOR2_TILE:
-                       case R_0280CC_CB_COLOR3_TILE:
-                       case R_0280D0_CB_COLOR4_TILE:
-                       case R_0280D4_CB_COLOR5_TILE:
-                       case R_0280D8_CB_COLOR6_TILE:
-                       case R_0280DC_CB_COLOR7_TILE:
-                               if (!r600_cs_packet_next_is_pkt3_nop(p)) {
-                                       if (!track->cb_color0_base_last) {
-                                               dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
-                                               return -EINVAL;
-                                       }
-                                       ib[idx+1+i] = track->cb_color0_base_last;
-                                       printk_once(KERN_WARNING "radeon: You have old & broken userspace "
-                                               "please consider updating mesa & xf86-video-ati\n");
-                               } else {
-                                       r = r600_cs_packet_next_reloc(p, &reloc);
-                                       if (r) {
-                                               dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
-                                               return -EINVAL;
-                                       }
-                                       ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
-                               }
-                               break;
-                       case DB_DEPTH_BASE:
-                       case DB_HTILE_DATA_BASE:
-                       case CB_COLOR0_BASE:
-                               r = r600_cs_packet_next_reloc(p, &reloc);
-                               if (r) {
-                                       DRM_ERROR("bad SET_CONTEXT_REG "
-                                                       "0x%04X\n", reg);
-                                       return -EINVAL;
-                               }
-                               ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
-                               track->cb_color0_base_last = ib[idx+1+i];
-                               break;
-                       case CB_COLOR1_BASE:
-                       case CB_COLOR2_BASE:
-                       case CB_COLOR3_BASE:
-                       case CB_COLOR4_BASE:
-                       case CB_COLOR5_BASE:
-                       case CB_COLOR6_BASE:
-                       case CB_COLOR7_BASE:
-                       case SQ_PGM_START_FS:
-                       case SQ_PGM_START_ES:
-                       case SQ_PGM_START_VS:
-                       case SQ_PGM_START_GS:
-                       case SQ_PGM_START_PS:
-                               r = r600_cs_packet_next_reloc(p, &reloc);
-                               if (r) {
-                                       DRM_ERROR("bad SET_CONTEXT_REG "
-                                                       "0x%04X\n", reg);
-                                       return -EINVAL;
-                               }
-                               ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
-                               break;
-                       case VGT_DMA_BASE:
-                       case VGT_DMA_BASE_HI:
-                               /* These should be handled by DRAW_INDEX packet 3 */
-                       case VGT_STRMOUT_BASE_OFFSET_0:
-                       case VGT_STRMOUT_BASE_OFFSET_1:
-                       case VGT_STRMOUT_BASE_OFFSET_2:
-                       case VGT_STRMOUT_BASE_OFFSET_3:
-                       case VGT_STRMOUT_BASE_OFFSET_HI_0:
-                       case VGT_STRMOUT_BASE_OFFSET_HI_1:
-                       case VGT_STRMOUT_BASE_OFFSET_HI_2:
-                       case VGT_STRMOUT_BASE_OFFSET_HI_3:
-                       case VGT_STRMOUT_BUFFER_BASE_0:
-                       case VGT_STRMOUT_BUFFER_BASE_1:
-                       case VGT_STRMOUT_BUFFER_BASE_2:
-                       case VGT_STRMOUT_BUFFER_BASE_3:
-                       case VGT_STRMOUT_BUFFER_OFFSET_0:
-                       case VGT_STRMOUT_BUFFER_OFFSET_1:
-                       case VGT_STRMOUT_BUFFER_OFFSET_2:
-                       case VGT_STRMOUT_BUFFER_OFFSET_3:
-                               /* These should be handled by STRMOUT_BUFFER packet 3 */
-                               DRM_ERROR("bad context reg: 0x%08x\n", reg);
-                               return -EINVAL;
-                       default:
-                               break;
-                       }
+                       r = r600_cs_check_reg(p, reg, idx+1+i);
+                       if (r)
+                               return r;
                }
                break;
        case PACKET3_SET_RESOURCE:
@@ -646,6 +1174,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                        return -EINVAL;
                }
                for (i = 0; i < (pkt->count / 7); i++) {
+                       struct radeon_bo *texture, *mipmap;
+                       u32 size, offset;
+
                        switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) {
                        case SQ_TEX_VTX_VALID_TEXTURE:
                                /* tex base */
@@ -655,6 +1186,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                                        return -EINVAL;
                                }
                                ib[idx+1+(i*7)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+                               texture = reloc->robj;
                                /* tex mip base */
                                r = r600_cs_packet_next_reloc(p, &reloc);
                                if (r) {
@@ -662,6 +1194,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                                        return -EINVAL;
                                }
                                ib[idx+1+(i*7)+3] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+                               mipmap = reloc->robj;
+                               r = r600_check_texture_resource(p,  idx+(i*7)+1,
+                                               texture, mipmap);
+                               if (r)
+                                       return r;
                                break;
                        case SQ_TEX_VTX_VALID_BUFFER:
                                /* vtx base */
@@ -670,6 +1207,13 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                                        DRM_ERROR("bad SET_RESOURCE\n");
                                        return -EINVAL;
                                }
+                               offset = radeon_get_ib_value(p, idx+1+(i*7)+0);
+                               size = radeon_get_ib_value(p, idx+1+(i*7)+1);
+                               if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
+                                       /* force size to size of the buffer */
+                                       dev_warn(p->dev, "vbo resource seems too big for the bo\n");
+                                       ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj);
+                               }
                                ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
                                ib[idx+1+(i*7)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
                                break;
@@ -760,11 +1304,28 @@ int r600_cs_parse(struct radeon_cs_parser *p)
        struct r600_cs_track *track;
        int r;
 
-       track = kzalloc(sizeof(*track), GFP_KERNEL);
-       p->track = track;
+       if (p->track == NULL) {
+               /* initialize tracker, we are in kms */
+               track = kzalloc(sizeof(*track), GFP_KERNEL);
+               if (track == NULL)
+                       return -ENOMEM;
+               r600_cs_track_init(track);
+               if (p->rdev->family < CHIP_RV770) {
+                       track->npipes = p->rdev->config.r600.tiling_npipes;
+                       track->nbanks = p->rdev->config.r600.tiling_nbanks;
+                       track->group_size = p->rdev->config.r600.tiling_group_size;
+               } else if (p->rdev->family <= CHIP_RV740) {
+                       track->npipes = p->rdev->config.rv770.tiling_npipes;
+                       track->nbanks = p->rdev->config.rv770.tiling_nbanks;
+                       track->group_size = p->rdev->config.rv770.tiling_group_size;
+               }
+               p->track = track;
+       }
        do {
                r = r600_cs_packet_parse(p, &pkt, p->idx);
                if (r) {
+                       kfree(p->track);
+                       p->track = NULL;
                        return r;
                }
                p->idx += pkt.count + 2;
@@ -779,9 +1340,13 @@ int r600_cs_parse(struct radeon_cs_parser *p)
                        break;
                default:
                        DRM_ERROR("Unknown packet type %d !\n", pkt.type);
+                       kfree(p->track);
+                       p->track = NULL;
                        return -EINVAL;
                }
                if (r) {
+                       kfree(p->track);
+                       p->track = NULL;
                        return r;
                }
        } while (p->idx < p->chunks[p->chunk_ib_idx].length_dw);
@@ -791,6 +1356,8 @@ int r600_cs_parse(struct radeon_cs_parser *p)
                mdelay(1);
        }
 #endif
+       kfree(p->track);
+       p->track = NULL;
        return 0;
 }
 
@@ -833,9 +1400,16 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp,
 {
        struct radeon_cs_parser parser;
        struct radeon_cs_chunk *ib_chunk;
-       struct radeon_ib        fake_ib;
+       struct radeon_ib fake_ib;
+       struct r600_cs_track *track;
        int r;
 
+       /* initialize tracker */
+       track = kzalloc(sizeof(*track), GFP_KERNEL);
+       if (track == NULL)
+               return -ENOMEM;
+       r600_cs_track_init(track);
+       r600_cs_legacy_get_tiling_conf(dev, &track->npipes, &track->nbanks, &track->group_size);
        /* initialize parser */
        memset(&parser, 0, sizeof(struct radeon_cs_parser));
        parser.filp = filp;
@@ -843,6 +1417,7 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp,
        parser.rdev = NULL;
        parser.family = family;
        parser.ib = &fake_ib;
+       parser.track = track;
        fake_ib.ptr = ib;
        r = radeon_cs_parser_init(&parser, data);
        if (r) {
index 30480881aed18e62e5033a3d7c06832b321f2993..5b2e4d44282394ba85598c2ae6a91bd7ffc42b79 100644 (file)
 
 #define R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL          0x5480
 
+#define R_028C04_PA_SC_AA_CONFIG                     0x028C04
+#define   S_028C04_MSAA_NUM_SAMPLES(x)                 (((x) & 0x3) << 0)
+#define   G_028C04_MSAA_NUM_SAMPLES(x)                 (((x) >> 0) & 0x3)
+#define   C_028C04_MSAA_NUM_SAMPLES                    0xFFFFFFFC
+#define   S_028C04_AA_MASK_CENTROID_DTMN(x)            (((x) & 0x1) << 4)
+#define   G_028C04_AA_MASK_CENTROID_DTMN(x)            (((x) >> 4) & 0x1)
+#define   C_028C04_AA_MASK_CENTROID_DTMN               0xFFFFFFEF
+#define   S_028C04_MAX_SAMPLE_DIST(x)                  (((x) & 0xF) << 13)
+#define   G_028C04_MAX_SAMPLE_DIST(x)                  (((x) >> 13) & 0xF)
+#define   C_028C04_MAX_SAMPLE_DIST                     0xFFFE1FFF
 #define R_0280E0_CB_COLOR0_FRAG                      0x0280E0
 #define   S_0280E0_BASE_256B(x)                        (((x) & 0xFFFFFFFF) << 0)
 #define   G_0280E0_BASE_256B(x)                        (((x) >> 0) & 0xFFFFFFFF)
 #define R_0280D4_CB_COLOR5_TILE                      0x0280D4
 #define R_0280D8_CB_COLOR6_TILE                      0x0280D8
 #define R_0280DC_CB_COLOR7_TILE                      0x0280DC
-
+#define R_0280A0_CB_COLOR0_INFO                      0x0280A0
+#define   S_0280A0_ENDIAN(x)                           (((x) & 0x3) << 0)
+#define   G_0280A0_ENDIAN(x)                           (((x) >> 0) & 0x3)
+#define   C_0280A0_ENDIAN                              0xFFFFFFFC
+#define   S_0280A0_FORMAT(x)                           (((x) & 0x3F) << 2)
+#define   G_0280A0_FORMAT(x)                           (((x) >> 2) & 0x3F)
+#define   C_0280A0_FORMAT                              0xFFFFFF03
+#define     V_0280A0_COLOR_INVALID                     0x00000000
+#define     V_0280A0_COLOR_8                           0x00000001
+#define     V_0280A0_COLOR_4_4                         0x00000002
+#define     V_0280A0_COLOR_3_3_2                       0x00000003
+#define     V_0280A0_COLOR_16                          0x00000005
+#define     V_0280A0_COLOR_16_FLOAT                    0x00000006
+#define     V_0280A0_COLOR_8_8                         0x00000007
+#define     V_0280A0_COLOR_5_6_5                       0x00000008
+#define     V_0280A0_COLOR_6_5_5                       0x00000009
+#define     V_0280A0_COLOR_1_5_5_5                     0x0000000A
+#define     V_0280A0_COLOR_4_4_4_4                     0x0000000B
+#define     V_0280A0_COLOR_5_5_5_1                     0x0000000C
+#define     V_0280A0_COLOR_32                          0x0000000D
+#define     V_0280A0_COLOR_32_FLOAT                    0x0000000E
+#define     V_0280A0_COLOR_16_16                       0x0000000F
+#define     V_0280A0_COLOR_16_16_FLOAT                 0x00000010
+#define     V_0280A0_COLOR_8_24                        0x00000011
+#define     V_0280A0_COLOR_8_24_FLOAT                  0x00000012
+#define     V_0280A0_COLOR_24_8                        0x00000013
+#define     V_0280A0_COLOR_24_8_FLOAT                  0x00000014
+#define     V_0280A0_COLOR_10_11_11                    0x00000015
+#define     V_0280A0_COLOR_10_11_11_FLOAT              0x00000016
+#define     V_0280A0_COLOR_11_11_10                    0x00000017
+#define     V_0280A0_COLOR_11_11_10_FLOAT              0x00000018
+#define     V_0280A0_COLOR_2_10_10_10                  0x00000019
+#define     V_0280A0_COLOR_8_8_8_8                     0x0000001A
+#define     V_0280A0_COLOR_10_10_10_2                  0x0000001B
+#define     V_0280A0_COLOR_X24_8_32_FLOAT              0x0000001C
+#define     V_0280A0_COLOR_32_32                       0x0000001D
+#define     V_0280A0_COLOR_32_32_FLOAT                 0x0000001E
+#define     V_0280A0_COLOR_16_16_16_16                 0x0000001F
+#define     V_0280A0_COLOR_16_16_16_16_FLOAT           0x00000020
+#define     V_0280A0_COLOR_32_32_32_32                 0x00000022
+#define     V_0280A0_COLOR_32_32_32_32_FLOAT           0x00000023
+#define   S_0280A0_ARRAY_MODE(x)                       (((x) & 0xF) << 8)
+#define   G_0280A0_ARRAY_MODE(x)                       (((x) >> 8) & 0xF)
+#define   C_0280A0_ARRAY_MODE                          0xFFFFF0FF
+#define     V_0280A0_ARRAY_LINEAR_GENERAL              0x00000000
+#define     V_0280A0_ARRAY_LINEAR_ALIGNED              0x00000001
+#define     V_0280A0_ARRAY_1D_TILED_THIN1              0x00000002
+#define     V_0280A0_ARRAY_2D_TILED_THIN1              0x00000004
+#define   S_0280A0_NUMBER_TYPE(x)                      (((x) & 0x7) << 12)
+#define   G_0280A0_NUMBER_TYPE(x)                      (((x) >> 12) & 0x7)
+#define   C_0280A0_NUMBER_TYPE                         0xFFFF8FFF
+#define   S_0280A0_READ_SIZE(x)                        (((x) & 0x1) << 15)
+#define   G_0280A0_READ_SIZE(x)                        (((x) >> 15) & 0x1)
+#define   C_0280A0_READ_SIZE                           0xFFFF7FFF
+#define   S_0280A0_COMP_SWAP(x)                        (((x) & 0x3) << 16)
+#define   G_0280A0_COMP_SWAP(x)                        (((x) >> 16) & 0x3)
+#define   C_0280A0_COMP_SWAP                           0xFFFCFFFF
+#define   S_0280A0_TILE_MODE(x)                        (((x) & 0x3) << 18)
+#define   G_0280A0_TILE_MODE(x)                        (((x) >> 18) & 0x3)
+#define   C_0280A0_TILE_MODE                           0xFFF3FFFF
+#define   S_0280A0_BLEND_CLAMP(x)                      (((x) & 0x1) << 20)
+#define   G_0280A0_BLEND_CLAMP(x)                      (((x) >> 20) & 0x1)
+#define   C_0280A0_BLEND_CLAMP                         0xFFEFFFFF
+#define   S_0280A0_CLEAR_COLOR(x)                      (((x) & 0x1) << 21)
+#define   G_0280A0_CLEAR_COLOR(x)                      (((x) >> 21) & 0x1)
+#define   C_0280A0_CLEAR_COLOR                         0xFFDFFFFF
+#define   S_0280A0_BLEND_BYPASS(x)                     (((x) & 0x1) << 22)
+#define   G_0280A0_BLEND_BYPASS(x)                     (((x) >> 22) & 0x1)
+#define   C_0280A0_BLEND_BYPASS                        0xFFBFFFFF
+#define   S_0280A0_BLEND_FLOAT32(x)                    (((x) & 0x1) << 23)
+#define   G_0280A0_BLEND_FLOAT32(x)                    (((x) >> 23) & 0x1)
+#define   C_0280A0_BLEND_FLOAT32                       0xFF7FFFFF
+#define   S_0280A0_SIMPLE_FLOAT(x)                     (((x) & 0x1) << 24)
+#define   G_0280A0_SIMPLE_FLOAT(x)                     (((x) >> 24) & 0x1)
+#define   C_0280A0_SIMPLE_FLOAT                        0xFEFFFFFF
+#define   S_0280A0_ROUND_MODE(x)                       (((x) & 0x1) << 25)
+#define   G_0280A0_ROUND_MODE(x)                       (((x) >> 25) & 0x1)
+#define   C_0280A0_ROUND_MODE                          0xFDFFFFFF
+#define   S_0280A0_TILE_COMPACT(x)                     (((x) & 0x1) << 26)
+#define   G_0280A0_TILE_COMPACT(x)                     (((x) >> 26) & 0x1)
+#define   C_0280A0_TILE_COMPACT                        0xFBFFFFFF
+#define   S_0280A0_SOURCE_FORMAT(x)                    (((x) & 0x1) << 27)
+#define   G_0280A0_SOURCE_FORMAT(x)                    (((x) >> 27) & 0x1)
+#define   C_0280A0_SOURCE_FORMAT                       0xF7FFFFFF
+#define R_0280A4_CB_COLOR1_INFO                      0x0280A4
+#define R_0280A8_CB_COLOR2_INFO                      0x0280A8
+#define R_0280AC_CB_COLOR3_INFO                      0x0280AC
+#define R_0280B0_CB_COLOR4_INFO                      0x0280B0
+#define R_0280B4_CB_COLOR5_INFO                      0x0280B4
+#define R_0280B8_CB_COLOR6_INFO                      0x0280B8
+#define R_0280BC_CB_COLOR7_INFO                      0x0280BC
+#define R_028060_CB_COLOR0_SIZE                      0x028060
+#define   S_028060_PITCH_TILE_MAX(x)                   (((x) & 0x3FF) << 0)
+#define   G_028060_PITCH_TILE_MAX(x)                   (((x) >> 0) & 0x3FF)
+#define   C_028060_PITCH_TILE_MAX                      0xFFFFFC00
+#define   S_028060_SLICE_TILE_MAX(x)                   (((x) & 0xFFFFF) << 10)
+#define   G_028060_SLICE_TILE_MAX(x)                   (((x) >> 10) & 0xFFFFF)
+#define   C_028060_SLICE_TILE_MAX                      0xC00003FF
+#define R_028064_CB_COLOR1_SIZE                      0x028064
+#define R_028068_CB_COLOR2_SIZE                      0x028068
+#define R_02806C_CB_COLOR3_SIZE                      0x02806C
+#define R_028070_CB_COLOR4_SIZE                      0x028070
+#define R_028074_CB_COLOR5_SIZE                      0x028074
+#define R_028078_CB_COLOR6_SIZE                      0x028078
+#define R_02807C_CB_COLOR7_SIZE                      0x02807C
+#define R_028238_CB_TARGET_MASK                      0x028238
+#define   S_028238_TARGET0_ENABLE(x)                   (((x) & 0xF) << 0)
+#define   G_028238_TARGET0_ENABLE(x)                   (((x) >> 0) & 0xF)
+#define   C_028238_TARGET0_ENABLE                      0xFFFFFFF0
+#define   S_028238_TARGET1_ENABLE(x)                   (((x) & 0xF) << 4)
+#define   G_028238_TARGET1_ENABLE(x)                   (((x) >> 4) & 0xF)
+#define   C_028238_TARGET1_ENABLE                      0xFFFFFF0F
+#define   S_028238_TARGET2_ENABLE(x)                   (((x) & 0xF) << 8)
+#define   G_028238_TARGET2_ENABLE(x)                   (((x) >> 8) & 0xF)
+#define   C_028238_TARGET2_ENABLE                      0xFFFFF0FF
+#define   S_028238_TARGET3_ENABLE(x)                   (((x) & 0xF) << 12)
+#define   G_028238_TARGET3_ENABLE(x)                   (((x) >> 12) & 0xF)
+#define   C_028238_TARGET3_ENABLE                      0xFFFF0FFF
+#define   S_028238_TARGET4_ENABLE(x)                   (((x) & 0xF) << 16)
+#define   G_028238_TARGET4_ENABLE(x)                   (((x) >> 16) & 0xF)
+#define   C_028238_TARGET4_ENABLE                      0xFFF0FFFF
+#define   S_028238_TARGET5_ENABLE(x)                   (((x) & 0xF) << 20)
+#define   G_028238_TARGET5_ENABLE(x)                   (((x) >> 20) & 0xF)
+#define   C_028238_TARGET5_ENABLE                      0xFF0FFFFF
+#define   S_028238_TARGET6_ENABLE(x)                   (((x) & 0xF) << 24)
+#define   G_028238_TARGET6_ENABLE(x)                   (((x) >> 24) & 0xF)
+#define   C_028238_TARGET6_ENABLE                      0xF0FFFFFF
+#define   S_028238_TARGET7_ENABLE(x)                   (((x) & 0xF) << 28)
+#define   G_028238_TARGET7_ENABLE(x)                   (((x) >> 28) & 0xF)
+#define   C_028238_TARGET7_ENABLE                      0x0FFFFFFF
+#define R_02823C_CB_SHADER_MASK                      0x02823C
+#define   S_02823C_OUTPUT0_ENABLE(x)                   (((x) & 0xF) << 0)
+#define   G_02823C_OUTPUT0_ENABLE(x)                   (((x) >> 0) & 0xF)
+#define   C_02823C_OUTPUT0_ENABLE                      0xFFFFFFF0
+#define   S_02823C_OUTPUT1_ENABLE(x)                   (((x) & 0xF) << 4)
+#define   G_02823C_OUTPUT1_ENABLE(x)                   (((x) >> 4) & 0xF)
+#define   C_02823C_OUTPUT1_ENABLE                      0xFFFFFF0F
+#define   S_02823C_OUTPUT2_ENABLE(x)                   (((x) & 0xF) << 8)
+#define   G_02823C_OUTPUT2_ENABLE(x)                   (((x) >> 8) & 0xF)
+#define   C_02823C_OUTPUT2_ENABLE                      0xFFFFF0FF
+#define   S_02823C_OUTPUT3_ENABLE(x)                   (((x) & 0xF) << 12)
+#define   G_02823C_OUTPUT3_ENABLE(x)                   (((x) >> 12) & 0xF)
+#define   C_02823C_OUTPUT3_ENABLE                      0xFFFF0FFF
+#define   S_02823C_OUTPUT4_ENABLE(x)                   (((x) & 0xF) << 16)
+#define   G_02823C_OUTPUT4_ENABLE(x)                   (((x) >> 16) & 0xF)
+#define   C_02823C_OUTPUT4_ENABLE                      0xFFF0FFFF
+#define   S_02823C_OUTPUT5_ENABLE(x)                   (((x) & 0xF) << 20)
+#define   G_02823C_OUTPUT5_ENABLE(x)                   (((x) >> 20) & 0xF)
+#define   C_02823C_OUTPUT5_ENABLE                      0xFF0FFFFF
+#define   S_02823C_OUTPUT6_ENABLE(x)                   (((x) & 0xF) << 24)
+#define   G_02823C_OUTPUT6_ENABLE(x)                   (((x) >> 24) & 0xF)
+#define   C_02823C_OUTPUT6_ENABLE                      0xF0FFFFFF
+#define   S_02823C_OUTPUT7_ENABLE(x)                   (((x) & 0xF) << 28)
+#define   G_02823C_OUTPUT7_ENABLE(x)                   (((x) >> 28) & 0xF)
+#define   C_02823C_OUTPUT7_ENABLE                      0x0FFFFFFF
+#define R_028AB0_VGT_STRMOUT_EN                      0x028AB0
+#define   S_028AB0_STREAMOUT(x)                        (((x) & 0x1) << 0)
+#define   G_028AB0_STREAMOUT(x)                        (((x) >> 0) & 0x1)
+#define   C_028AB0_STREAMOUT                           0xFFFFFFFE
+#define R_028B20_VGT_STRMOUT_BUFFER_EN               0x028B20
+#define   S_028B20_BUFFER_0_EN(x)                      (((x) & 0x1) << 0)
+#define   G_028B20_BUFFER_0_EN(x)                      (((x) >> 0) & 0x1)
+#define   C_028B20_BUFFER_0_EN                         0xFFFFFFFE
+#define   S_028B20_BUFFER_1_EN(x)                      (((x) & 0x1) << 1)
+#define   G_028B20_BUFFER_1_EN(x)                      (((x) >> 1) & 0x1)
+#define   C_028B20_BUFFER_1_EN                         0xFFFFFFFD
+#define   S_028B20_BUFFER_2_EN(x)                      (((x) & 0x1) << 2)
+#define   G_028B20_BUFFER_2_EN(x)                      (((x) >> 2) & 0x1)
+#define   C_028B20_BUFFER_2_EN                         0xFFFFFFFB
+#define   S_028B20_BUFFER_3_EN(x)                      (((x) & 0x1) << 3)
+#define   G_028B20_BUFFER_3_EN(x)                      (((x) >> 3) & 0x1)
+#define   C_028B20_BUFFER_3_EN                         0xFFFFFFF7
+#define   S_028B20_SIZE(x)                             (((x) & 0xFFFFFFFF) << 0)
+#define   G_028B20_SIZE(x)                             (((x) >> 0) & 0xFFFFFFFF)
+#define   C_028B20_SIZE                                0x00000000
+#define R_038000_SQ_TEX_RESOURCE_WORD0_0             0x038000
+#define   S_038000_DIM(x)                              (((x) & 0x7) << 0)
+#define   G_038000_DIM(x)                              (((x) >> 0) & 0x7)
+#define   C_038000_DIM                                 0xFFFFFFF8
+#define     V_038000_SQ_TEX_DIM_1D                     0x00000000
+#define     V_038000_SQ_TEX_DIM_2D                     0x00000001
+#define     V_038000_SQ_TEX_DIM_3D                     0x00000002
+#define     V_038000_SQ_TEX_DIM_CUBEMAP                0x00000003
+#define     V_038000_SQ_TEX_DIM_1D_ARRAY               0x00000004
+#define     V_038000_SQ_TEX_DIM_2D_ARRAY               0x00000005
+#define     V_038000_SQ_TEX_DIM_2D_MSAA                0x00000006
+#define     V_038000_SQ_TEX_DIM_2D_ARRAY_MSAA          0x00000007
+#define   S_038000_TILE_MODE(x)                        (((x) & 0xF) << 3)
+#define   G_038000_TILE_MODE(x)                        (((x) >> 3) & 0xF)
+#define   C_038000_TILE_MODE                           0xFFFFFF87
+#define   S_038000_TILE_TYPE(x)                        (((x) & 0x1) << 7)
+#define   G_038000_TILE_TYPE(x)                        (((x) >> 7) & 0x1)
+#define   C_038000_TILE_TYPE                           0xFFFFFF7F
+#define   S_038000_PITCH(x)                            (((x) & 0x7FF) << 8)
+#define   G_038000_PITCH(x)                            (((x) >> 8) & 0x7FF)
+#define   C_038000_PITCH                               0xFFF800FF
+#define   S_038000_TEX_WIDTH(x)                        (((x) & 0x1FFF) << 19)
+#define   G_038000_TEX_WIDTH(x)                        (((x) >> 19) & 0x1FFF)
+#define   C_038000_TEX_WIDTH                           0x0007FFFF
+#define R_038004_SQ_TEX_RESOURCE_WORD1_0             0x038004
+#define   S_038004_TEX_HEIGHT(x)                       (((x) & 0x1FFF) << 0)
+#define   G_038004_TEX_HEIGHT(x)                       (((x) >> 0) & 0x1FFF)
+#define   C_038004_TEX_HEIGHT                          0xFFFFE000
+#define   S_038004_TEX_DEPTH(x)                        (((x) & 0x1FFF) << 13)
+#define   G_038004_TEX_DEPTH(x)                        (((x) >> 13) & 0x1FFF)
+#define   C_038004_TEX_DEPTH                           0xFC001FFF
+#define   S_038004_DATA_FORMAT(x)                      (((x) & 0x3F) << 26)
+#define   G_038004_DATA_FORMAT(x)                      (((x) >> 26) & 0x3F)
+#define   C_038004_DATA_FORMAT                         0x03FFFFFF
+#define     V_038004_COLOR_INVALID                     0x00000000
+#define     V_038004_COLOR_8                           0x00000001
+#define     V_038004_COLOR_4_4                         0x00000002
+#define     V_038004_COLOR_3_3_2                       0x00000003
+#define     V_038004_COLOR_16                          0x00000005
+#define     V_038004_COLOR_16_FLOAT                    0x00000006
+#define     V_038004_COLOR_8_8                         0x00000007
+#define     V_038004_COLOR_5_6_5                       0x00000008
+#define     V_038004_COLOR_6_5_5                       0x00000009
+#define     V_038004_COLOR_1_5_5_5                     0x0000000A
+#define     V_038004_COLOR_4_4_4_4                     0x0000000B
+#define     V_038004_COLOR_5_5_5_1                     0x0000000C
+#define     V_038004_COLOR_32                          0x0000000D
+#define     V_038004_COLOR_32_FLOAT                    0x0000000E
+#define     V_038004_COLOR_16_16                       0x0000000F
+#define     V_038004_COLOR_16_16_FLOAT                 0x00000010
+#define     V_038004_COLOR_8_24                        0x00000011
+#define     V_038004_COLOR_8_24_FLOAT                  0x00000012
+#define     V_038004_COLOR_24_8                        0x00000013
+#define     V_038004_COLOR_24_8_FLOAT                  0x00000014
+#define     V_038004_COLOR_10_11_11                    0x00000015
+#define     V_038004_COLOR_10_11_11_FLOAT              0x00000016
+#define     V_038004_COLOR_11_11_10                    0x00000017
+#define     V_038004_COLOR_11_11_10_FLOAT              0x00000018
+#define     V_038004_COLOR_2_10_10_10                  0x00000019
+#define     V_038004_COLOR_8_8_8_8                     0x0000001A
+#define     V_038004_COLOR_10_10_10_2                  0x0000001B
+#define     V_038004_COLOR_X24_8_32_FLOAT              0x0000001C
+#define     V_038004_COLOR_32_32                       0x0000001D
+#define     V_038004_COLOR_32_32_FLOAT                 0x0000001E
+#define     V_038004_COLOR_16_16_16_16                 0x0000001F
+#define     V_038004_COLOR_16_16_16_16_FLOAT           0x00000020
+#define     V_038004_COLOR_32_32_32_32                 0x00000022
+#define     V_038004_COLOR_32_32_32_32_FLOAT           0x00000023
+#define     V_038004_FMT_1                             0x00000025
+#define     V_038004_FMT_GB_GR                         0x00000027
+#define     V_038004_FMT_BG_RG                         0x00000028
+#define     V_038004_FMT_32_AS_8                       0x00000029
+#define     V_038004_FMT_32_AS_8_8                     0x0000002A
+#define     V_038004_FMT_5_9_9_9_SHAREDEXP             0x0000002B
+#define     V_038004_FMT_8_8_8                         0x0000002C
+#define     V_038004_FMT_16_16_16                      0x0000002D
+#define     V_038004_FMT_16_16_16_FLOAT                0x0000002E
+#define     V_038004_FMT_32_32_32                      0x0000002F
+#define     V_038004_FMT_32_32_32_FLOAT                0x00000030
+#define R_038010_SQ_TEX_RESOURCE_WORD4_0             0x038010
+#define   S_038010_FORMAT_COMP_X(x)                    (((x) & 0x3) << 0)
+#define   G_038010_FORMAT_COMP_X(x)                    (((x) >> 0) & 0x3)
+#define   C_038010_FORMAT_COMP_X                       0xFFFFFFFC
+#define   S_038010_FORMAT_COMP_Y(x)                    (((x) & 0x3) << 2)
+#define   G_038010_FORMAT_COMP_Y(x)                    (((x) >> 2) & 0x3)
+#define   C_038010_FORMAT_COMP_Y                       0xFFFFFFF3
+#define   S_038010_FORMAT_COMP_Z(x)                    (((x) & 0x3) << 4)
+#define   G_038010_FORMAT_COMP_Z(x)                    (((x) >> 4) & 0x3)
+#define   C_038010_FORMAT_COMP_Z                       0xFFFFFFCF
+#define   S_038010_FORMAT_COMP_W(x)                    (((x) & 0x3) << 6)
+#define   G_038010_FORMAT_COMP_W(x)                    (((x) >> 6) & 0x3)
+#define   C_038010_FORMAT_COMP_W                       0xFFFFFF3F
+#define   S_038010_NUM_FORMAT_ALL(x)                   (((x) & 0x3) << 8)
+#define   G_038010_NUM_FORMAT_ALL(x)                   (((x) >> 8) & 0x3)
+#define   C_038010_NUM_FORMAT_ALL                      0xFFFFFCFF
+#define   S_038010_SRF_MODE_ALL(x)                     (((x) & 0x1) << 10)
+#define   G_038010_SRF_MODE_ALL(x)                     (((x) >> 10) & 0x1)
+#define   C_038010_SRF_MODE_ALL                        0xFFFFFBFF
+#define   S_038010_FORCE_DEGAMMA(x)                    (((x) & 0x1) << 11)
+#define   G_038010_FORCE_DEGAMMA(x)                    (((x) >> 11) & 0x1)
+#define   C_038010_FORCE_DEGAMMA                       0xFFFFF7FF
+#define   S_038010_ENDIAN_SWAP(x)                      (((x) & 0x3) << 12)
+#define   G_038010_ENDIAN_SWAP(x)                      (((x) >> 12) & 0x3)
+#define   C_038010_ENDIAN_SWAP                         0xFFFFCFFF
+#define   S_038010_REQUEST_SIZE(x)                     (((x) & 0x3) << 14)
+#define   G_038010_REQUEST_SIZE(x)                     (((x) >> 14) & 0x3)
+#define   C_038010_REQUEST_SIZE                        0xFFFF3FFF
+#define   S_038010_DST_SEL_X(x)                        (((x) & 0x7) << 16)
+#define   G_038010_DST_SEL_X(x)                        (((x) >> 16) & 0x7)
+#define   C_038010_DST_SEL_X                           0xFFF8FFFF
+#define   S_038010_DST_SEL_Y(x)                        (((x) & 0x7) << 19)
+#define   G_038010_DST_SEL_Y(x)                        (((x) >> 19) & 0x7)
+#define   C_038010_DST_SEL_Y                           0xFFC7FFFF
+#define   S_038010_DST_SEL_Z(x)                        (((x) & 0x7) << 22)
+#define   G_038010_DST_SEL_Z(x)                        (((x) >> 22) & 0x7)
+#define   C_038010_DST_SEL_Z                           0xFE3FFFFF
+#define   S_038010_DST_SEL_W(x)                        (((x) & 0x7) << 25)
+#define   G_038010_DST_SEL_W(x)                        (((x) >> 25) & 0x7)
+#define   C_038010_DST_SEL_W                           0xF1FFFFFF
+#define   S_038010_BASE_LEVEL(x)                       (((x) & 0xF) << 28)
+#define   G_038010_BASE_LEVEL(x)                       (((x) >> 28) & 0xF)
+#define   C_038010_BASE_LEVEL                          0x0FFFFFFF
+#define R_038014_SQ_TEX_RESOURCE_WORD5_0             0x038014
+#define   S_038014_LAST_LEVEL(x)                       (((x) & 0xF) << 0)
+#define   G_038014_LAST_LEVEL(x)                       (((x) >> 0) & 0xF)
+#define   C_038014_LAST_LEVEL                          0xFFFFFFF0
+#define   S_038014_BASE_ARRAY(x)                       (((x) & 0x1FFF) << 4)
+#define   G_038014_BASE_ARRAY(x)                       (((x) >> 4) & 0x1FFF)
+#define   C_038014_BASE_ARRAY                          0xFFFE000F
+#define   S_038014_LAST_ARRAY(x)                       (((x) & 0x1FFF) << 17)
+#define   G_038014_LAST_ARRAY(x)                       (((x) >> 17) & 0x1FFF)
+#define   C_038014_LAST_ARRAY                          0xC001FFFF
+#define R_0288A8_SQ_ESGS_RING_ITEMSIZE               0x0288A8
+#define   S_0288A8_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288A8_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288A8_ITEMSIZE                            0xFFFF8000
+#define R_008C44_SQ_ESGS_RING_SIZE                   0x008C44
+#define   S_008C44_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C44_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C44_MEM_SIZE                            0x00000000
+#define R_0288B0_SQ_ESTMP_RING_ITEMSIZE              0x0288B0
+#define   S_0288B0_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288B0_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288B0_ITEMSIZE                            0xFFFF8000
+#define R_008C54_SQ_ESTMP_RING_SIZE                  0x008C54
+#define   S_008C54_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C54_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C54_MEM_SIZE                            0x00000000
+#define R_0288C0_SQ_FBUF_RING_ITEMSIZE               0x0288C0
+#define   S_0288C0_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288C0_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288C0_ITEMSIZE                            0xFFFF8000
+#define R_008C74_SQ_FBUF_RING_SIZE                   0x008C74
+#define   S_008C74_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C74_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C74_MEM_SIZE                            0x00000000
+#define R_0288B4_SQ_GSTMP_RING_ITEMSIZE              0x0288B4
+#define   S_0288B4_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288B4_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288B4_ITEMSIZE                            0xFFFF8000
+#define R_008C5C_SQ_GSTMP_RING_SIZE                  0x008C5C
+#define   S_008C5C_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C5C_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C5C_MEM_SIZE                            0x00000000
+#define R_0288AC_SQ_GSVS_RING_ITEMSIZE               0x0288AC
+#define   S_0288AC_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288AC_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288AC_ITEMSIZE                            0xFFFF8000
+#define R_008C4C_SQ_GSVS_RING_SIZE                   0x008C4C
+#define   S_008C4C_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C4C_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C4C_MEM_SIZE                            0x00000000
+#define R_0288BC_SQ_PSTMP_RING_ITEMSIZE              0x0288BC
+#define   S_0288BC_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288BC_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288BC_ITEMSIZE                            0xFFFF8000
+#define R_008C6C_SQ_PSTMP_RING_SIZE                  0x008C6C
+#define   S_008C6C_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C6C_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C6C_MEM_SIZE                            0x00000000
+#define R_0288C4_SQ_REDUC_RING_ITEMSIZE              0x0288C4
+#define   S_0288C4_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288C4_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288C4_ITEMSIZE                            0xFFFF8000
+#define R_008C7C_SQ_REDUC_RING_SIZE                  0x008C7C
+#define   S_008C7C_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C7C_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C7C_MEM_SIZE                            0x00000000
+#define R_0288B8_SQ_VSTMP_RING_ITEMSIZE              0x0288B8
+#define   S_0288B8_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288B8_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288B8_ITEMSIZE                            0xFFFF8000
+#define R_008C64_SQ_VSTMP_RING_SIZE                  0x008C64
+#define   S_008C64_MEM_SIZE(x)                         (((x) & 0xFFFFFFFF) << 0)
+#define   G_008C64_MEM_SIZE(x)                         (((x) >> 0) & 0xFFFFFFFF)
+#define   C_008C64_MEM_SIZE                            0x00000000
+#define R_0288C8_SQ_GS_VERT_ITEMSIZE                 0x0288C8
+#define   S_0288C8_ITEMSIZE(x)                         (((x) & 0x7FFF) << 0)
+#define   G_0288C8_ITEMSIZE(x)                         (((x) >> 0) & 0x7FFF)
+#define   C_0288C8_ITEMSIZE                            0xFFFF8000
+#define R_028010_DB_DEPTH_INFO                       0x028010
+#define   S_028010_FORMAT(x)                           (((x) & 0x7) << 0)
+#define   G_028010_FORMAT(x)                           (((x) >> 0) & 0x7)
+#define   C_028010_FORMAT                              0xFFFFFFF8
+#define     V_028010_DEPTH_INVALID                     0x00000000
+#define     V_028010_DEPTH_16                          0x00000001
+#define     V_028010_DEPTH_X8_24                       0x00000002
+#define     V_028010_DEPTH_8_24                        0x00000003
+#define     V_028010_DEPTH_X8_24_FLOAT                 0x00000004
+#define     V_028010_DEPTH_8_24_FLOAT                  0x00000005
+#define     V_028010_DEPTH_32_FLOAT                    0x00000006
+#define     V_028010_DEPTH_X24_8_32_FLOAT              0x00000007
+#define   S_028010_READ_SIZE(x)                        (((x) & 0x1) << 3)
+#define   G_028010_READ_SIZE(x)                        (((x) >> 3) & 0x1)
+#define   C_028010_READ_SIZE                           0xFFFFFFF7
+#define   S_028010_ARRAY_MODE(x)                       (((x) & 0xF) << 15)
+#define   G_028010_ARRAY_MODE(x)                       (((x) >> 15) & 0xF)
+#define   C_028010_ARRAY_MODE                          0xFFF87FFF
+#define   S_028010_TILE_SURFACE_ENABLE(x)              (((x) & 0x1) << 25)
+#define   G_028010_TILE_SURFACE_ENABLE(x)              (((x) >> 25) & 0x1)
+#define   C_028010_TILE_SURFACE_ENABLE                 0xFDFFFFFF
+#define   S_028010_TILE_COMPACT(x)                     (((x) & 0x1) << 26)
+#define   G_028010_TILE_COMPACT(x)                     (((x) >> 26) & 0x1)
+#define   C_028010_TILE_COMPACT                        0xFBFFFFFF
+#define   S_028010_ZRANGE_PRECISION(x)                 (((x) & 0x1) << 31)
+#define   G_028010_ZRANGE_PRECISION(x)                 (((x) >> 31) & 0x1)
+#define   C_028010_ZRANGE_PRECISION                    0x7FFFFFFF
+#define R_028000_DB_DEPTH_SIZE                       0x028000
+#define   S_028000_PITCH_TILE_MAX(x)                   (((x) & 0x3FF) << 0)
+#define   G_028000_PITCH_TILE_MAX(x)                   (((x) >> 0) & 0x3FF)
+#define   C_028000_PITCH_TILE_MAX                      0xFFFFFC00
+#define   S_028000_SLICE_TILE_MAX(x)                   (((x) & 0xFFFFF) << 10)
+#define   G_028000_SLICE_TILE_MAX(x)                   (((x) >> 10) & 0xFFFFF)
+#define   C_028000_SLICE_TILE_MAX                      0xC00003FF
+#define R_028004_DB_DEPTH_VIEW                       0x028004
+#define   S_028004_SLICE_START(x)                      (((x) & 0x7FF) << 0)
+#define   G_028004_SLICE_START(x)                      (((x) >> 0) & 0x7FF)
+#define   C_028004_SLICE_START                         0xFFFFF800
+#define   S_028004_SLICE_MAX(x)                        (((x) & 0x7FF) << 13)
+#define   G_028004_SLICE_MAX(x)                        (((x) >> 13) & 0x7FF)
+#define   C_028004_SLICE_MAX                           0xFF001FFF
+#define R_028800_DB_DEPTH_CONTROL                    0x028800
+#define   S_028800_STENCIL_ENABLE(x)                   (((x) & 0x1) << 0)
+#define   G_028800_STENCIL_ENABLE(x)                   (((x) >> 0) & 0x1)
+#define   C_028800_STENCIL_ENABLE                      0xFFFFFFFE
+#define   S_028800_Z_ENABLE(x)                         (((x) & 0x1) << 1)
+#define   G_028800_Z_ENABLE(x)                         (((x) >> 1) & 0x1)
+#define   C_028800_Z_ENABLE                            0xFFFFFFFD
+#define   S_028800_Z_WRITE_ENABLE(x)                   (((x) & 0x1) << 2)
+#define   G_028800_Z_WRITE_ENABLE(x)                   (((x) >> 2) & 0x1)
+#define   C_028800_Z_WRITE_ENABLE                      0xFFFFFFFB
+#define   S_028800_ZFUNC(x)                            (((x) & 0x7) << 4)
+#define   G_028800_ZFUNC(x)                            (((x) >> 4) & 0x7)
+#define   C_028800_ZFUNC                               0xFFFFFF8F
+#define   S_028800_BACKFACE_ENABLE(x)                  (((x) & 0x1) << 7)
+#define   G_028800_BACKFACE_ENABLE(x)                  (((x) >> 7) & 0x1)
+#define   C_028800_BACKFACE_ENABLE                     0xFFFFFF7F
+#define   S_028800_STENCILFUNC(x)                      (((x) & 0x7) << 8)
+#define   G_028800_STENCILFUNC(x)                      (((x) >> 8) & 0x7)
+#define   C_028800_STENCILFUNC                         0xFFFFF8FF
+#define   S_028800_STENCILFAIL(x)                      (((x) & 0x7) << 11)
+#define   G_028800_STENCILFAIL(x)                      (((x) >> 11) & 0x7)
+#define   C_028800_STENCILFAIL                         0xFFFFC7FF
+#define   S_028800_STENCILZPASS(x)                     (((x) & 0x7) << 14)
+#define   G_028800_STENCILZPASS(x)                     (((x) >> 14) & 0x7)
+#define   C_028800_STENCILZPASS                        0xFFFE3FFF
+#define   S_028800_STENCILZFAIL(x)                     (((x) & 0x7) << 17)
+#define   G_028800_STENCILZFAIL(x)                     (((x) >> 17) & 0x7)
+#define   C_028800_STENCILZFAIL                        0xFFF1FFFF
+#define   S_028800_STENCILFUNC_BF(x)                   (((x) & 0x7) << 20)
+#define   G_028800_STENCILFUNC_BF(x)                   (((x) >> 20) & 0x7)
+#define   C_028800_STENCILFUNC_BF                      0xFF8FFFFF
+#define   S_028800_STENCILFAIL_BF(x)                   (((x) & 0x7) << 23)
+#define   G_028800_STENCILFAIL_BF(x)                   (((x) >> 23) & 0x7)
+#define   C_028800_STENCILFAIL_BF                      0xFC7FFFFF
+#define   S_028800_STENCILZPASS_BF(x)                  (((x) & 0x7) << 26)
+#define   G_028800_STENCILZPASS_BF(x)                  (((x) >> 26) & 0x7)
+#define   C_028800_STENCILZPASS_BF                     0xE3FFFFFF
+#define   S_028800_STENCILZFAIL_BF(x)                  (((x) & 0x7) << 29)
+#define   G_028800_STENCILZFAIL_BF(x)                  (((x) >> 29) & 0x7)
+#define   C_028800_STENCILZFAIL_BF                     0x1FFFFFFF
 
 #endif
index c0356bb193e57f39ef602ad50c6af607b056c15a..829e26e8a4bb877f43dc2b8f12646e5d840753cb 100644 (file)
@@ -89,6 +89,7 @@ extern int radeon_testing;
 extern int radeon_connector_table;
 extern int radeon_tv;
 extern int radeon_new_pll;
+extern int radeon_dynpm;
 extern int radeon_audio;
 
 /*
@@ -118,6 +119,21 @@ struct radeon_device;
 /*
  * BIOS.
  */
+#define ATRM_BIOS_PAGE 4096
+
+#if defined(CONFIG_VGA_SWITCHEROO)
+bool radeon_atrm_supported(struct pci_dev *pdev);
+int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len);
+#else
+static inline bool radeon_atrm_supported(struct pci_dev *pdev)
+{
+       return false;
+}
+
+static inline int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len){
+       return -EINVAL;
+}
+#endif
 bool radeon_get_bios(struct radeon_device *rdev);
 
 
@@ -138,17 +154,23 @@ void radeon_dummy_page_fini(struct radeon_device *rdev);
 struct radeon_clock {
        struct radeon_pll p1pll;
        struct radeon_pll p2pll;
+       struct radeon_pll dcpll;
        struct radeon_pll spll;
        struct radeon_pll mpll;
        /* 10 Khz units */
        uint32_t default_mclk;
        uint32_t default_sclk;
+       uint32_t default_dispclk;
+       uint32_t dp_extclk;
 };
 
 /*
  * Power management
  */
 int radeon_pm_init(struct radeon_device *rdev);
+void radeon_pm_compute_clocks(struct radeon_device *rdev);
+void radeon_combios_get_power_modes(struct radeon_device *rdev);
+void radeon_atombios_get_power_modes(struct radeon_device *rdev);
 
 /*
  * Fences.
@@ -275,6 +297,7 @@ union radeon_gart_table {
 };
 
 #define RADEON_GPU_PAGE_SIZE 4096
+#define RADEON_GPU_PAGE_MASK (RADEON_GPU_PAGE_SIZE - 1)
 
 struct radeon_gart {
        dma_addr_t                      table_addr;
@@ -309,21 +332,19 @@ struct radeon_mc {
        /* for some chips with <= 32MB we need to lie
         * about vram size near mc fb location */
        u64                     mc_vram_size;
-       u64                     gtt_location;
+       u64                     visible_vram_size;
        u64                     gtt_size;
        u64                     gtt_start;
        u64                     gtt_end;
-       u64                     vram_location;
        u64                     vram_start;
        u64                     vram_end;
        unsigned                vram_width;
        u64                     real_vram_size;
        int                     vram_mtrr;
        bool                    vram_is_ddr;
-       bool                    igp_sideport_enabled;
+       bool                    igp_sideport_enabled;
 };
 
-int radeon_mc_setup(struct radeon_device *rdev);
 bool radeon_combios_sideport_present(struct radeon_device *rdev);
 bool radeon_atombios_sideport_present(struct radeon_device *rdev);
 
@@ -348,6 +369,7 @@ struct radeon_irq {
        bool            sw_int;
        /* FIXME: use a define max crtc rather than hardcode it */
        bool            crtc_vblank_int[2];
+       wait_queue_head_t       vblank_queue;
        /* FIXME: use defines for max hpd/dacs */
        bool            hpd[6];
        spinlock_t sw_lock;
@@ -379,6 +401,7 @@ struct radeon_ib {
 struct radeon_ib_pool {
        struct mutex            mutex;
        struct radeon_bo        *robj;
+       struct list_head        bogus_ib;
        struct radeon_ib        ibs[RADEON_IB_POOL_SIZE];
        bool                    ready;
        unsigned                head_id;
@@ -433,6 +456,7 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib);
 int radeon_ib_pool_init(struct radeon_device *rdev);
 void radeon_ib_pool_fini(struct radeon_device *rdev);
 int radeon_ib_test(struct radeon_device *rdev);
+extern void radeon_ib_bogus_add(struct radeon_device *rdev, struct radeon_ib *ib);
 /* Ring access between begin & end cannot sleep */
 void radeon_ring_free_size(struct radeon_device *rdev);
 int radeon_ring_lock(struct radeon_device *rdev, unsigned ndw);
@@ -570,7 +594,99 @@ struct radeon_wb {
  * Equation between gpu/memory clock and available bandwidth is hw dependent
  * (type of memory, bus size, efficiency, ...)
  */
+enum radeon_pm_state {
+       PM_STATE_DISABLED,
+       PM_STATE_MINIMUM,
+       PM_STATE_PAUSED,
+       PM_STATE_ACTIVE
+};
+enum radeon_pm_action {
+       PM_ACTION_NONE,
+       PM_ACTION_MINIMUM,
+       PM_ACTION_DOWNCLOCK,
+       PM_ACTION_UPCLOCK
+};
+
+enum radeon_voltage_type {
+       VOLTAGE_NONE = 0,
+       VOLTAGE_GPIO,
+       VOLTAGE_VDDC,
+       VOLTAGE_SW
+};
+
+enum radeon_pm_state_type {
+       POWER_STATE_TYPE_DEFAULT,
+       POWER_STATE_TYPE_POWERSAVE,
+       POWER_STATE_TYPE_BATTERY,
+       POWER_STATE_TYPE_BALANCED,
+       POWER_STATE_TYPE_PERFORMANCE,
+};
+
+enum radeon_pm_clock_mode_type {
+       POWER_MODE_TYPE_DEFAULT,
+       POWER_MODE_TYPE_LOW,
+       POWER_MODE_TYPE_MID,
+       POWER_MODE_TYPE_HIGH,
+};
+
+struct radeon_voltage {
+       enum radeon_voltage_type type;
+       /* gpio voltage */
+       struct radeon_gpio_rec gpio;
+       u32 delay; /* delay in usec from voltage drop to sclk change */
+       bool active_high; /* voltage drop is active when bit is high */
+       /* VDDC voltage */
+       u8 vddc_id; /* index into vddc voltage table */
+       u8 vddci_id; /* index into vddci voltage table */
+       bool vddci_enabled;
+       /* r6xx+ sw */
+       u32 voltage;
+};
+
+struct radeon_pm_non_clock_info {
+       /* pcie lanes */
+       int pcie_lanes;
+       /* standardized non-clock flags */
+       u32 flags;
+};
+
+struct radeon_pm_clock_info {
+       /* memory clock */
+       u32 mclk;
+       /* engine clock */
+       u32 sclk;
+       /* voltage info */
+       struct radeon_voltage voltage;
+       /* standardized clock flags - not sure we'll need these */
+       u32 flags;
+};
+
+struct radeon_power_state {
+       enum radeon_pm_state_type type;
+       /* XXX: use a define for num clock modes */
+       struct radeon_pm_clock_info clock_info[8];
+       /* number of valid clock modes in this power state */
+       int num_clock_modes;
+       struct radeon_pm_clock_info *default_clock_mode;
+       /* non clock info about this state */
+       struct radeon_pm_non_clock_info non_clock_info;
+       bool voltage_drop_active;
+};
+
+/*
+ * Some modes are overclocked by very low value, accept them
+ */
+#define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */
+
 struct radeon_pm {
+       struct mutex            mutex;
+       struct delayed_work     idle_work;
+       enum radeon_pm_state    state;
+       enum radeon_pm_action   planned_action;
+       unsigned long           action_timeout;
+       bool                    downclocked;
+       int                     active_crtcs;
+       int                     req_vblank;
        fixed20_12              max_bandwidth;
        fixed20_12              igp_sideport_mclk;
        fixed20_12              igp_system_mclk;
@@ -582,6 +698,15 @@ struct radeon_pm {
        fixed20_12              core_bandwidth;
        fixed20_12              sclk;
        fixed20_12              needed_bandwidth;
+       /* XXX: use a define for num power modes */
+       struct radeon_power_state power_state[8];
+       /* number of valid power states */
+       int                     num_power_states;
+       struct radeon_power_state *current_power_state;
+       struct radeon_pm_clock_info *current_clock_mode;
+       struct radeon_power_state *requested_power_state;
+       struct radeon_pm_clock_info *requested_clock_mode;
+       struct radeon_power_state *default_power_state;
 };
 
 
@@ -651,6 +776,7 @@ struct radeon_asic {
        void (*set_engine_clock)(struct radeon_device *rdev, uint32_t eng_clock);
        uint32_t (*get_memory_clock)(struct radeon_device *rdev);
        void (*set_memory_clock)(struct radeon_device *rdev, uint32_t mem_clock);
+       int (*get_pcie_lanes)(struct radeon_device *rdev);
        void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes);
        void (*set_clock_gating)(struct radeon_device *rdev, int enable);
        int (*set_surface_reg)(struct radeon_device *rdev, int reg,
@@ -701,6 +827,9 @@ struct r600_asic {
        unsigned sx_max_export_pos_size;
        unsigned sx_max_export_smx_size;
        unsigned sq_num_cf_insts;
+       unsigned tiling_nbanks;
+       unsigned tiling_npipes;
+       unsigned tiling_group_size;
 };
 
 struct rv770_asic {
@@ -721,6 +850,9 @@ struct rv770_asic {
        unsigned sc_prim_fifo_size;
        unsigned sc_hiz_tile_fifo_size;
        unsigned sc_earlyz_tile_fifo_fize;
+       unsigned tiling_nbanks;
+       unsigned tiling_npipes;
+       unsigned tiling_group_size;
 };
 
 union radeon_asic_config {
@@ -830,6 +962,8 @@ struct radeon_device {
        struct r600_ih ih; /* r6/700 interrupt ring */
        struct workqueue_struct *wq;
        struct work_struct hotplug_work;
+       int num_crtc; /* number of crtcs */
+       struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
 
        /* audio stuff */
        struct timer_list       audio_timer;
@@ -838,6 +972,8 @@ struct radeon_device {
        int                     audio_bits_per_sample;
        uint8_t                 audio_status_bits;
        uint8_t                 audio_category_code;
+
+       bool powered_down;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
@@ -895,6 +1031,8 @@ static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32
 #define WREG32_MC(reg, v) rdev->mc_wreg(rdev, (reg), (v))
 #define RREG32_PCIE(reg) rv370_pcie_rreg(rdev, (reg))
 #define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v))
+#define RREG32_PCIE_P(reg) rdev->pciep_rreg(rdev, (reg))
+#define WREG32_PCIE_P(reg, v) rdev->pciep_wreg(rdev, (reg), (v))
 #define WREG32_P(reg, val, mask)                               \
        do {                                                    \
                uint32_t tmp_ = RREG32(reg);                    \
@@ -956,7 +1094,7 @@ void r100_pll_errata_after_index(struct radeon_device *rdev);
 #define ASIC_IS_AVIVO(rdev) ((rdev->family >= CHIP_RS600))
 #define ASIC_IS_DCE3(rdev) ((rdev->family >= CHIP_RV620))
 #define ASIC_IS_DCE32(rdev) ((rdev->family >= CHIP_RV730))
-
+#define ASIC_IS_DCE4(rdev) ((rdev->family >= CHIP_CEDAR))
 
 /*
  * BIOS helpers.
@@ -1015,6 +1153,7 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
 #define radeon_set_engine_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
 #define radeon_get_memory_clock(rdev) (rdev)->asic->get_memory_clock((rdev))
 #define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_memory_clock((rdev), (e))
+#define radeon_get_pcie_lanes(rdev) (rdev)->asic->get_pcie_lanes((rdev))
 #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l))
 #define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e))
 #define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->set_surface_reg((rdev), (r), (f), (p), (o), (s)))
@@ -1029,6 +1168,7 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
 /* AGP */
 extern void radeon_agp_disable(struct radeon_device *rdev);
 extern int radeon_gart_table_vram_pin(struct radeon_device *rdev);
+extern void radeon_gart_restore(struct radeon_device *rdev);
 extern int radeon_modeset_init(struct radeon_device *rdev);
 extern void radeon_modeset_fini(struct radeon_device *rdev);
 extern bool radeon_card_posted(struct radeon_device *rdev);
@@ -1042,6 +1182,10 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl
 extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
 extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
 extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
+extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
+extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
+extern int radeon_resume_kms(struct drm_device *dev);
+extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
 
 /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
 struct r100_mc_save {
@@ -1096,7 +1240,7 @@ extern void r200_set_safe_registers(struct radeon_device *rdev);
 /* r300,r350,rv350,rv370,rv380 */
 extern void r300_set_reg_safe(struct radeon_device *rdev);
 extern void r300_mc_program(struct radeon_device *rdev);
-extern void r300_vram_info(struct radeon_device *rdev);
+extern void r300_mc_init(struct radeon_device *rdev);
 extern void r300_clock_startup(struct radeon_device *rdev);
 extern int r300_mc_wait_for_idle(struct radeon_device *rdev);
 extern int rv370_pcie_gart_init(struct radeon_device *rdev);
@@ -1105,7 +1249,6 @@ extern int rv370_pcie_gart_enable(struct radeon_device *rdev);
 extern void rv370_pcie_gart_disable(struct radeon_device *rdev);
 
 /* r420,r423,rv410 */
-extern int r420_mc_init(struct radeon_device *rdev);
 extern u32 r420_mc_rreg(struct radeon_device *rdev, u32 reg);
 extern void r420_mc_wreg(struct radeon_device *rdev, u32 reg, u32 v);
 extern int r420_debugfs_pipes_info_init(struct radeon_device *rdev);
@@ -1147,13 +1290,13 @@ extern void rs690_line_buffer_adjust(struct radeon_device *rdev,
                                        struct drm_display_mode *mode2);
 
 /* r600, rv610, rv630, rv620, rv635, rv670, rs780, rs880 */
+extern void r600_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
 extern bool r600_card_posted(struct radeon_device *rdev);
 extern void r600_cp_stop(struct radeon_device *rdev);
 extern void r600_ring_init(struct radeon_device *rdev, unsigned ring_size);
 extern int r600_cp_resume(struct radeon_device *rdev);
 extern void r600_cp_fini(struct radeon_device *rdev);
 extern int r600_count_pipe_bits(uint32_t val);
-extern int r600_gart_clear_page(struct radeon_device *rdev, int i);
 extern int r600_mc_wait_for_idle(struct radeon_device *rdev);
 extern int r600_pcie_gart_init(struct radeon_device *rdev);
 extern void r600_pcie_gart_tlb_flush(struct radeon_device *rdev);
@@ -1189,6 +1332,14 @@ extern void r600_hdmi_update_audio_settings(struct drm_encoder *encoder,
                                            uint8_t status_bits,
                                            uint8_t category_code);
 
+/* evergreen */
+struct evergreen_mc_save {
+       u32 vga_control[6];
+       u32 vga_render_control;
+       u32 vga_hdp_control;
+       u32 crtc_control[6];
+};
+
 #include "radeon_object.h"
 
 #endif
index c0681a5556dc96bc0bbbeec86774c3d23098ad35..c4457791dff1ae606004572d52b1d69eea1f7052 100644 (file)
@@ -237,6 +237,10 @@ int radeon_agp_init(struct radeon_device *rdev)
 
        rdev->mc.agp_base = rdev->ddev->agp->agp_info.aper_base;
        rdev->mc.gtt_size = rdev->ddev->agp->agp_info.aper_size << 20;
+       rdev->mc.gtt_start = rdev->mc.agp_base;
+       rdev->mc.gtt_end = rdev->mc.gtt_start + rdev->mc.gtt_size - 1;
+       dev_info(rdev->dev, "GTT: %lluM 0x%08llX - 0x%08llX\n",
+               rdev->mc.gtt_size >> 20, rdev->mc.gtt_start, rdev->mc.gtt_end);
 
        /* workaround some hw issues */
        if (rdev->family < CHIP_R200) {
index 05ee1aeac3fdce21cd72f3735c156be2c0ab2198..d3a157b2bcb73ab51e1d68d0430c65a9916a70b0 100644 (file)
@@ -43,7 +43,7 @@ void radeon_atom_set_memory_clock(struct radeon_device *rdev, uint32_t mem_clock
 void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
 
 /*
- * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
+ * r100,rv100,rs100,rv200,rs200
  */
 extern int r100_init(struct radeon_device *rdev);
 extern void r100_fini(struct radeon_device *rdev);
@@ -108,6 +108,52 @@ static struct radeon_asic r100_asic = {
        .set_engine_clock = &radeon_legacy_set_engine_clock,
        .get_memory_clock = &radeon_legacy_get_memory_clock,
        .set_memory_clock = NULL,
+       .get_pcie_lanes = NULL,
+       .set_pcie_lanes = NULL,
+       .set_clock_gating = &radeon_legacy_set_clock_gating,
+       .set_surface_reg = r100_set_surface_reg,
+       .clear_surface_reg = r100_clear_surface_reg,
+       .bandwidth_update = &r100_bandwidth_update,
+       .hpd_init = &r100_hpd_init,
+       .hpd_fini = &r100_hpd_fini,
+       .hpd_sense = &r100_hpd_sense,
+       .hpd_set_polarity = &r100_hpd_set_polarity,
+       .ioctl_wait_idle = NULL,
+};
+
+/*
+ * r200,rv250,rs300,rv280
+ */
+extern int r200_copy_dma(struct radeon_device *rdev,
+                       uint64_t src_offset,
+                       uint64_t dst_offset,
+                       unsigned num_pages,
+                       struct radeon_fence *fence);
+static struct radeon_asic r200_asic = {
+       .init = &r100_init,
+       .fini = &r100_fini,
+       .suspend = &r100_suspend,
+       .resume = &r100_resume,
+       .vga_set_state = &r100_vga_set_state,
+       .gpu_reset = &r100_gpu_reset,
+       .gart_tlb_flush = &r100_pci_gart_tlb_flush,
+       .gart_set_page = &r100_pci_gart_set_page,
+       .cp_commit = &r100_cp_commit,
+       .ring_start = &r100_ring_start,
+       .ring_test = &r100_ring_test,
+       .ring_ib_execute = &r100_ring_ib_execute,
+       .irq_set = &r100_irq_set,
+       .irq_process = &r100_irq_process,
+       .get_vblank_counter = &r100_get_vblank_counter,
+       .fence_ring_emit = &r100_fence_ring_emit,
+       .cs_parse = &r100_cs_parse,
+       .copy_blit = &r100_copy_blit,
+       .copy_dma = &r200_copy_dma,
+       .copy = &r100_copy_blit,
+       .get_engine_clock = &radeon_legacy_get_engine_clock,
+       .set_engine_clock = &radeon_legacy_set_engine_clock,
+       .get_memory_clock = &radeon_legacy_get_memory_clock,
+       .set_memory_clock = NULL,
        .set_pcie_lanes = NULL,
        .set_clock_gating = &radeon_legacy_set_clock_gating,
        .set_surface_reg = r100_set_surface_reg,
@@ -138,11 +184,8 @@ extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t
 extern uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg);
 extern void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
 extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes);
-extern int r300_copy_dma(struct radeon_device *rdev,
-                       uint64_t src_offset,
-                       uint64_t dst_offset,
-                       unsigned num_pages,
-                       struct radeon_fence *fence);
+extern int rv370_get_pcie_lanes(struct radeon_device *rdev);
+
 static struct radeon_asic r300_asic = {
        .init = &r300_init,
        .fini = &r300_fini,
@@ -162,7 +205,46 @@ static struct radeon_asic r300_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
+       .copy = &r100_copy_blit,
+       .get_engine_clock = &radeon_legacy_get_engine_clock,
+       .set_engine_clock = &radeon_legacy_set_engine_clock,
+       .get_memory_clock = &radeon_legacy_get_memory_clock,
+       .set_memory_clock = NULL,
+       .get_pcie_lanes = &rv370_get_pcie_lanes,
+       .set_pcie_lanes = &rv370_set_pcie_lanes,
+       .set_clock_gating = &radeon_legacy_set_clock_gating,
+       .set_surface_reg = r100_set_surface_reg,
+       .clear_surface_reg = r100_clear_surface_reg,
+       .bandwidth_update = &r100_bandwidth_update,
+       .hpd_init = &r100_hpd_init,
+       .hpd_fini = &r100_hpd_fini,
+       .hpd_sense = &r100_hpd_sense,
+       .hpd_set_polarity = &r100_hpd_set_polarity,
+       .ioctl_wait_idle = NULL,
+};
+
+
+static struct radeon_asic r300_asic_pcie = {
+       .init = &r300_init,
+       .fini = &r300_fini,
+       .suspend = &r300_suspend,
+       .resume = &r300_resume,
+       .vga_set_state = &r100_vga_set_state,
+       .gpu_reset = &r300_gpu_reset,
+       .gart_tlb_flush = &rv370_pcie_gart_tlb_flush,
+       .gart_set_page = &rv370_pcie_gart_set_page,
+       .cp_commit = &r100_cp_commit,
+       .ring_start = &r300_ring_start,
+       .ring_test = &r100_ring_test,
+       .ring_ib_execute = &r100_ring_ib_execute,
+       .irq_set = &r100_irq_set,
+       .irq_process = &r100_irq_process,
+       .get_vblank_counter = &r100_get_vblank_counter,
+       .fence_ring_emit = &r300_fence_ring_emit,
+       .cs_parse = &r300_cs_parse,
+       .copy_blit = &r100_copy_blit,
+       .copy_dma = &r200_copy_dma,
        .copy = &r100_copy_blit,
        .get_engine_clock = &radeon_legacy_get_engine_clock,
        .set_engine_clock = &radeon_legacy_set_engine_clock,
@@ -206,12 +288,13 @@ static struct radeon_asic r420_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
        .copy = &r100_copy_blit,
        .get_engine_clock = &radeon_atom_get_engine_clock,
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = &rv370_get_pcie_lanes,
        .set_pcie_lanes = &rv370_set_pcie_lanes,
        .set_clock_gating = &radeon_atom_set_clock_gating,
        .set_surface_reg = r100_set_surface_reg,
@@ -255,12 +338,13 @@ static struct radeon_asic rs400_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
        .copy = &r100_copy_blit,
        .get_engine_clock = &radeon_legacy_get_engine_clock,
        .set_engine_clock = &radeon_legacy_set_engine_clock,
        .get_memory_clock = &radeon_legacy_get_memory_clock,
        .set_memory_clock = NULL,
+       .get_pcie_lanes = NULL,
        .set_pcie_lanes = NULL,
        .set_clock_gating = &radeon_legacy_set_clock_gating,
        .set_surface_reg = r100_set_surface_reg,
@@ -314,14 +398,17 @@ static struct radeon_asic rs600_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
        .copy = &r100_copy_blit,
        .get_engine_clock = &radeon_atom_get_engine_clock,
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = NULL,
        .set_pcie_lanes = NULL,
        .set_clock_gating = &radeon_atom_set_clock_gating,
+       .set_surface_reg = r100_set_surface_reg,
+       .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &rs600_bandwidth_update,
        .hpd_init = &rs600_hpd_init,
        .hpd_fini = &rs600_hpd_fini,
@@ -360,12 +447,13 @@ static struct radeon_asic rs690_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
-       .copy = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
+       .copy = &r200_copy_dma,
        .get_engine_clock = &radeon_atom_get_engine_clock,
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = NULL,
        .set_pcie_lanes = NULL,
        .set_clock_gating = &radeon_atom_set_clock_gating,
        .set_surface_reg = r100_set_surface_reg,
@@ -412,12 +500,13 @@ static struct radeon_asic rv515_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
        .copy = &r100_copy_blit,
        .get_engine_clock = &radeon_atom_get_engine_clock,
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = &rv370_get_pcie_lanes,
        .set_pcie_lanes = &rv370_set_pcie_lanes,
        .set_clock_gating = &radeon_atom_set_clock_gating,
        .set_surface_reg = r100_set_surface_reg,
@@ -455,12 +544,13 @@ static struct radeon_asic r520_asic = {
        .fence_ring_emit = &r300_fence_ring_emit,
        .cs_parse = &r300_cs_parse,
        .copy_blit = &r100_copy_blit,
-       .copy_dma = &r300_copy_dma,
+       .copy_dma = &r200_copy_dma,
        .copy = &r100_copy_blit,
        .get_engine_clock = &radeon_atom_get_engine_clock,
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = &rv370_get_pcie_lanes,
        .set_pcie_lanes = &rv370_set_pcie_lanes,
        .set_clock_gating = &radeon_atom_set_clock_gating,
        .set_surface_reg = r100_set_surface_reg,
@@ -538,8 +628,9 @@ static struct radeon_asic r600_asic = {
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = &rv370_get_pcie_lanes,
        .set_pcie_lanes = NULL,
-       .set_clock_gating = &radeon_atom_set_clock_gating,
+       .set_clock_gating = NULL,
        .set_surface_reg = r600_set_surface_reg,
        .clear_surface_reg = r600_clear_surface_reg,
        .bandwidth_update = &rv515_bandwidth_update,
@@ -583,6 +674,7 @@ static struct radeon_asic rv770_asic = {
        .set_engine_clock = &radeon_atom_set_engine_clock,
        .get_memory_clock = &radeon_atom_get_memory_clock,
        .set_memory_clock = &radeon_atom_set_memory_clock,
+       .get_pcie_lanes = &rv370_get_pcie_lanes,
        .set_pcie_lanes = NULL,
        .set_clock_gating = &radeon_atom_set_clock_gating,
        .set_surface_reg = r600_set_surface_reg,
@@ -595,4 +687,54 @@ static struct radeon_asic rv770_asic = {
        .ioctl_wait_idle = r600_ioctl_wait_idle,
 };
 
+/*
+ * evergreen
+ */
+int evergreen_init(struct radeon_device *rdev);
+void evergreen_fini(struct radeon_device *rdev);
+int evergreen_suspend(struct radeon_device *rdev);
+int evergreen_resume(struct radeon_device *rdev);
+int evergreen_gpu_reset(struct radeon_device *rdev);
+void evergreen_bandwidth_update(struct radeon_device *rdev);
+void evergreen_hpd_init(struct radeon_device *rdev);
+void evergreen_hpd_fini(struct radeon_device *rdev);
+bool evergreen_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+void evergreen_hpd_set_polarity(struct radeon_device *rdev,
+                               enum radeon_hpd_id hpd);
+
+static struct radeon_asic evergreen_asic = {
+       .init = &evergreen_init,
+       .fini = &evergreen_fini,
+       .suspend = &evergreen_suspend,
+       .resume = &evergreen_resume,
+       .cp_commit = NULL,
+       .gpu_reset = &evergreen_gpu_reset,
+       .vga_set_state = &r600_vga_set_state,
+       .gart_tlb_flush = &r600_pcie_gart_tlb_flush,
+       .gart_set_page = &rs600_gart_set_page,
+       .ring_test = NULL,
+       .ring_ib_execute = NULL,
+       .irq_set = NULL,
+       .irq_process = NULL,
+       .get_vblank_counter = NULL,
+       .fence_ring_emit = NULL,
+       .cs_parse = NULL,
+       .copy_blit = NULL,
+       .copy_dma = NULL,
+       .copy = NULL,
+       .get_engine_clock = &radeon_atom_get_engine_clock,
+       .set_engine_clock = &radeon_atom_set_engine_clock,
+       .get_memory_clock = &radeon_atom_get_memory_clock,
+       .set_memory_clock = &radeon_atom_set_memory_clock,
+       .set_pcie_lanes = NULL,
+       .set_clock_gating = NULL,
+       .set_surface_reg = r600_set_surface_reg,
+       .clear_surface_reg = r600_clear_surface_reg,
+       .bandwidth_update = &evergreen_bandwidth_update,
+       .hpd_init = &evergreen_hpd_init,
+       .hpd_fini = &evergreen_hpd_fini,
+       .hpd_sense = &evergreen_hpd_sense,
+       .hpd_set_polarity = &evergreen_hpd_set_polarity,
+};
+
 #endif
index 4d8831548a5fc6f6b8e6f1522836853cf2e10832..93783b15c81d8226d9833c730af74250d8e3fb5e 100644 (file)
@@ -159,8 +159,15 @@ static struct radeon_hpd radeon_atom_get_hpd_info_from_gpio(struct radeon_device
                                                            struct radeon_gpio_rec *gpio)
 {
        struct radeon_hpd hpd;
+       u32 reg;
+
+       if (ASIC_IS_DCE4(rdev))
+               reg = EVERGREEN_DC_GPIO_HPD_A;
+       else
+               reg = AVIVO_DC_GPIO_HPD_A;
+
        hpd.gpio = *gpio;
-       if (gpio->reg == AVIVO_DC_GPIO_HPD_A) {
+       if (gpio->reg == reg) {
                switch(gpio->mask) {
                case (1 << 0):
                        hpd.hpd = RADEON_HPD_1;
@@ -574,6 +581,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
                                ddc_bus.valid = false;
                        }
 
+                       /* needed for aux chan transactions */
+                       ddc_bus.hpd_id = hpd.hpd ? (hpd.hpd - 1) : 0;
+
                        conn_id = le16_to_cpu(path->usConnObjectId);
 
                        if (!radeon_atom_apply_quirks
@@ -838,6 +848,7 @@ union firmware_info {
        ATOM_FIRMWARE_INFO_V1_2 info_12;
        ATOM_FIRMWARE_INFO_V1_3 info_13;
        ATOM_FIRMWARE_INFO_V1_4 info_14;
+       ATOM_FIRMWARE_INFO_V2_1 info_21;
 };
 
 bool radeon_atom_get_clock_info(struct drm_device *dev)
@@ -849,6 +860,7 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
        uint8_t frev, crev;
        struct radeon_pll *p1pll = &rdev->clock.p1pll;
        struct radeon_pll *p2pll = &rdev->clock.p2pll;
+       struct radeon_pll *dcpll = &rdev->clock.dcpll;
        struct radeon_pll *spll = &rdev->clock.spll;
        struct radeon_pll *mpll = &rdev->clock.mpll;
        uint16_t data_offset;
@@ -951,8 +963,19 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
                rdev->clock.default_mclk =
                    le32_to_cpu(firmware_info->info.ulDefaultMemoryClock);
 
+               if (ASIC_IS_DCE4(rdev)) {
+                       rdev->clock.default_dispclk =
+                               le32_to_cpu(firmware_info->info_21.ulDefaultDispEngineClkFreq);
+                       if (rdev->clock.default_dispclk == 0)
+                               rdev->clock.default_dispclk = 60000; /* 600 Mhz */
+                       rdev->clock.dp_extclk =
+                               le16_to_cpu(firmware_info->info_21.usUniphyDPModeExtClkFreq);
+               }
+               *dcpll = *p1pll;
+
                return true;
        }
+
        return false;
 }
 
@@ -1091,6 +1114,30 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
        return ss;
 }
 
+static void radeon_atom_apply_lvds_quirks(struct drm_device *dev,
+                                         struct radeon_encoder_atom_dig *lvds)
+{
+
+       /* Toshiba A300-1BU laptop panel doesn't like new pll divider algo */
+       if ((dev->pdev->device == 0x95c4) &&
+           (dev->pdev->subsystem_vendor == 0x1179) &&
+           (dev->pdev->subsystem_device == 0xff50)) {
+               if ((lvds->native_mode.hdisplay == 1280) &&
+                   (lvds->native_mode.vdisplay == 800))
+                       lvds->pll_algo = PLL_ALGO_LEGACY;
+       }
+
+       /* Dell Studio 15 laptop panel doesn't like new pll divider algo */
+       if ((dev->pdev->device == 0x95c4) &&
+           (dev->pdev->subsystem_vendor == 0x1028) &&
+           (dev->pdev->subsystem_device == 0x029f)) {
+               if ((lvds->native_mode.hdisplay == 1280) &&
+                   (lvds->native_mode.vdisplay == 800))
+                       lvds->pll_algo = PLL_ALGO_LEGACY;
+       }
+
+}
+
 union lvds_info {
        struct _ATOM_LVDS_INFO info;
        struct _ATOM_LVDS_INFO_V12 info_12;
@@ -1161,6 +1208,21 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
 
                lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id);
 
+               if (ASIC_IS_AVIVO(rdev)) {
+                       if (radeon_new_pll == 0)
+                               lvds->pll_algo = PLL_ALGO_LEGACY;
+                       else
+                               lvds->pll_algo = PLL_ALGO_NEW;
+               } else {
+                       if (radeon_new_pll == 1)
+                               lvds->pll_algo = PLL_ALGO_NEW;
+                       else
+                               lvds->pll_algo = PLL_ALGO_LEGACY;
+               }
+
+               /* LVDS quirks */
+               radeon_atom_apply_lvds_quirks(dev, lvds);
+
                encoder->native_mode = lvds->native_mode;
        }
        return lvds;
@@ -1385,20 +1447,375 @@ radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
        return tv_dac;
 }
 
-void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable)
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE info_4;
+};
+
+void radeon_atombios_get_power_modes(struct radeon_device *rdev)
 {
-       DYNAMIC_CLOCK_GATING_PS_ALLOCATION args;
-       int index = GetIndexIntoMasterTable(COMMAND, DynamicClockGating);
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+       u16 data_offset;
+       u8 frev, crev;
+       u32 misc, misc2 = 0, sclk, mclk;
+       union power_info *power_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       struct _ATOM_PPLIB_STATE *power_state;
+       int num_modes = 0, i, j;
+       int state_index = 0, mode_index = 0;
 
-       args.ucEnable = enable;
+       atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset);
 
-       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       rdev->pm.default_power_state = NULL;
+
+       if (power_info) {
+               if (frev < 4) {
+                       num_modes = power_info->info.ucNumOfPowerModeEntries;
+                       if (num_modes > ATOM_MAX_NUMBEROF_POWER_BLOCK)
+                               num_modes = ATOM_MAX_NUMBEROF_POWER_BLOCK;
+                       for (i = 0; i < num_modes; i++) {
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+                               switch (frev) {
+                               case 1:
+                                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                                       rdev->pm.power_state[state_index].clock_info[0].mclk =
+                                               le16_to_cpu(power_info->info.asPowerPlayInfo[i].usMemoryClock);
+                                       rdev->pm.power_state[state_index].clock_info[0].sclk =
+                                               le16_to_cpu(power_info->info.asPowerPlayInfo[i].usEngineClock);
+                                       /* skip invalid modes */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                                               continue;
+                                       /* skip overclock modes for now */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                                            rdev->clock.default_mclk + RADEON_MODE_OVERCLOCK_MARGIN) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                                            rdev->clock.default_sclk + RADEON_MODE_OVERCLOCK_MARGIN))
+                                               continue;
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               power_info->info.asPowerPlayInfo[i].ucNumPciELanes;
+                                       misc = le32_to_cpu(power_info->info.asPowerPlayInfo[i].ulMiscInfo);
+                                       if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_GPIO;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio =
+                                                       radeon_lookup_gpio(rdev,
+                                                       power_info->info.asPowerPlayInfo[i].ucVoltageDropIndex);
+                                               if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               true;
+                                               else
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               false;
+                                       } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_VDDC;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id =
+                                                       power_info->info.asPowerPlayInfo[i].ucVoltageDropIndex;
+                                       }
+                                       /* order matters! */
+                                       if (misc & ATOM_PM_MISCINFO_POWER_SAVING_MODE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_POWERSAVE;
+                                       if (misc & ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                       if (misc & ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                       if (misc & ATOM_PM_MISCINFO_LOAD_BALANCE_EN)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BALANCED;
+                                       if (misc & ATOM_PM_MISCINFO_3D_ACCELERATION_EN)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_PERFORMANCE;
+                                       if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) {
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_DEFAULT;
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                       }
+                                       state_index++;
+                                       break;
+                               case 2:
+                                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                                       rdev->pm.power_state[state_index].clock_info[0].mclk =
+                                               le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMemoryClock);
+                                       rdev->pm.power_state[state_index].clock_info[0].sclk =
+                                               le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulEngineClock);
+                                       /* skip invalid modes */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                                               continue;
+                                       /* skip overclock modes for now */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                                            rdev->clock.default_mclk + RADEON_MODE_OVERCLOCK_MARGIN) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                                            rdev->clock.default_sclk + RADEON_MODE_OVERCLOCK_MARGIN))
+                                               continue;
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               power_info->info_2.asPowerPlayInfo[i].ucNumPciELanes;
+                                       misc = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMiscInfo);
+                                       misc2 = le32_to_cpu(power_info->info_2.asPowerPlayInfo[i].ulMiscInfo2);
+                                       if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_GPIO;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio =
+                                                       radeon_lookup_gpio(rdev,
+                                                       power_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex);
+                                               if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               true;
+                                               else
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               false;
+                                       } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_VDDC;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id =
+                                                       power_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex;
+                                       }
+                                       /* order matters! */
+                                       if (misc & ATOM_PM_MISCINFO_POWER_SAVING_MODE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_POWERSAVE;
+                                       if (misc & ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                       if (misc & ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                       if (misc & ATOM_PM_MISCINFO_LOAD_BALANCE_EN)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BALANCED;
+                                       if (misc & ATOM_PM_MISCINFO_3D_ACCELERATION_EN)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_PERFORMANCE;
+                                       if (misc2 & ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BALANCED;
+                                       if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) {
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_DEFAULT;
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                       }
+                                       state_index++;
+                                       break;
+                               case 3:
+                                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                                       rdev->pm.power_state[state_index].clock_info[0].mclk =
+                                               le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMemoryClock);
+                                       rdev->pm.power_state[state_index].clock_info[0].sclk =
+                                               le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulEngineClock);
+                                       /* skip invalid modes */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                                               continue;
+                                       /* skip overclock modes for now */
+                                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                                            rdev->clock.default_mclk + RADEON_MODE_OVERCLOCK_MARGIN) ||
+                                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                                            rdev->clock.default_sclk + RADEON_MODE_OVERCLOCK_MARGIN))
+                                               continue;
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               power_info->info_3.asPowerPlayInfo[i].ucNumPciELanes;
+                                       misc = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMiscInfo);
+                                       misc2 = le32_to_cpu(power_info->info_3.asPowerPlayInfo[i].ulMiscInfo2);
+                                       if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_GPIO;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio =
+                                                       radeon_lookup_gpio(rdev,
+                                                       power_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex);
+                                               if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               true;
+                                               else
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                                               false;
+                                       } else if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.type =
+                                                       VOLTAGE_VDDC;
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.vddc_id =
+                                                       power_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex;
+                                               if (misc2 & ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN) {
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.vddci_enabled =
+                                                               true;
+                                                       rdev->pm.power_state[state_index].clock_info[0].voltage.vddci_id =
+                                                       power_info->info_3.asPowerPlayInfo[i].ucVDDCI_VoltageDropIndex;
+                                               }
+                                       }
+                                       /* order matters! */
+                                       if (misc & ATOM_PM_MISCINFO_POWER_SAVING_MODE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_POWERSAVE;
+                                       if (misc & ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                       if (misc & ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                       if (misc & ATOM_PM_MISCINFO_LOAD_BALANCE_EN)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BALANCED;
+                                       if (misc & ATOM_PM_MISCINFO_3D_ACCELERATION_EN)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_PERFORMANCE;
+                                       if (misc2 & ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE)
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BALANCED;
+                                       if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE) {
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_DEFAULT;
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[0];
+                                       }
+                                       state_index++;
+                                       break;
+                               }
+                       }
+               } else if (frev == 4) {
+                       for (i = 0; i < power_info->info_4.ucNumStates; i++) {
+                               mode_index = 0;
+                               power_state = (struct _ATOM_PPLIB_STATE *)
+                                       (mode_info->atom_context->bios +
+                                        data_offset +
+                                        le16_to_cpu(power_info->info_4.usStateArrayOffset) +
+                                        i * power_info->info_4.ucStateEntrySize);
+                               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                                       (mode_info->atom_context->bios +
+                                        data_offset +
+                                        le16_to_cpu(power_info->info_4.usNonClockInfoArrayOffset) +
+                                        (power_state->ucNonClockStateIndex *
+                                         power_info->info_4.ucNonClockSize));
+                               for (j = 0; j < (power_info->info_4.ucStateEntrySize - 1); j++) {
+                                       if (rdev->flags & RADEON_IS_IGP) {
+                                               struct _ATOM_PPLIB_RS780_CLOCK_INFO *clock_info =
+                                                       (struct _ATOM_PPLIB_RS780_CLOCK_INFO *)
+                                                       (mode_info->atom_context->bios +
+                                                        data_offset +
+                                                        le16_to_cpu(power_info->info_4.usClockInfoArrayOffset) +
+                                                        (power_state->ucClockStateIndices[j] *
+                                                         power_info->info_4.ucClockInfoSize));
+                                               sclk = le16_to_cpu(clock_info->usLowEngineClockLow);
+                                               sclk |= clock_info->ucLowEngineClockHigh << 16;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
+                                               /* skip invalid modes */
+                                               if (rdev->pm.power_state[state_index].clock_info[mode_index].sclk == 0)
+                                                       continue;
+                                               /* skip overclock modes for now */
+                                               if (rdev->pm.power_state[state_index].clock_info[mode_index].sclk >
+                                                   rdev->clock.default_sclk + RADEON_MODE_OVERCLOCK_MARGIN)
+                                                       continue;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type =
+                                                       VOLTAGE_SW;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
+                                                       clock_info->usVDDC;
+                                               mode_index++;
+                                       } else {
+                                               struct _ATOM_PPLIB_R600_CLOCK_INFO *clock_info =
+                                                       (struct _ATOM_PPLIB_R600_CLOCK_INFO *)
+                                                       (mode_info->atom_context->bios +
+                                                        data_offset +
+                                                        le16_to_cpu(power_info->info_4.usClockInfoArrayOffset) +
+                                                        (power_state->ucClockStateIndices[j] *
+                                                         power_info->info_4.ucClockInfoSize));
+                                               sclk = le16_to_cpu(clock_info->usEngineClockLow);
+                                               sclk |= clock_info->ucEngineClockHigh << 16;
+                                               mclk = le16_to_cpu(clock_info->usMemoryClockLow);
+                                               mclk |= clock_info->ucMemoryClockHigh << 16;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
+                                               /* skip invalid modes */
+                                               if ((rdev->pm.power_state[state_index].clock_info[mode_index].mclk == 0) ||
+                                                   (rdev->pm.power_state[state_index].clock_info[mode_index].sclk == 0))
+                                                       continue;
+                                               /* skip overclock modes for now */
+                                               if ((rdev->pm.power_state[state_index].clock_info[mode_index].mclk >
+                                                    rdev->clock.default_mclk + RADEON_MODE_OVERCLOCK_MARGIN) ||
+                                                   (rdev->pm.power_state[state_index].clock_info[mode_index].sclk >
+                                                    rdev->clock.default_sclk + RADEON_MODE_OVERCLOCK_MARGIN))
+                                                       continue;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type =
+                                                       VOLTAGE_SW;
+                                               rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
+                                                       clock_info->usVDDC;
+                                               mode_index++;
+                                       }
+                               }
+                               rdev->pm.power_state[state_index].num_clock_modes = mode_index;
+                               if (mode_index) {
+                                       misc = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+                                       misc2 = le16_to_cpu(non_clock_info->usClassification);
+                                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                               ((misc & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >>
+                                               ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT) + 1;
+                                       switch (misc2 & ATOM_PPLIB_CLASSIFICATION_UI_MASK) {
+                                       case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY:
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BATTERY;
+                                               break;
+                                       case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED:
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_BALANCED;
+                                               break;
+                                       case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE:
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_PERFORMANCE;
+                                               break;
+                                       }
+                                       if (misc2 & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+                                               rdev->pm.power_state[state_index].type =
+                                                       POWER_STATE_TYPE_DEFAULT;
+                                               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+                                               rdev->pm.power_state[state_index].default_clock_mode =
+                                                       &rdev->pm.power_state[state_index].clock_info[mode_index - 1];
+                                       }
+                                       state_index++;
+                               }
+                       }
+               }
+       } else {
+               /* XXX figure out some good default low power mode for cards w/out power tables */
+       }
+
+       if (rdev->pm.default_power_state == NULL) {
+               /* add the default mode */
+               rdev->pm.power_state[state_index].type =
+                       POWER_STATE_TYPE_DEFAULT;
+               rdev->pm.power_state[state_index].num_clock_modes = 1;
+               rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk;
+               rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
+               rdev->pm.power_state[state_index].default_clock_mode =
+                       &rdev->pm.power_state[state_index].clock_info[0];
+               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+               if (rdev->asic->get_pcie_lanes)
+                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = radeon_get_pcie_lanes(rdev);
+               else
+                       rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
+               rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+               state_index++;
+       }
+       rdev->pm.num_power_states = state_index;
+
+       rdev->pm.current_power_state = rdev->pm.default_power_state;
+       rdev->pm.current_clock_mode =
+               rdev->pm.default_power_state->default_clock_mode;
 }
 
-void radeon_atom_static_pwrmgt_setup(struct radeon_device *rdev, int enable)
+void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable)
 {
-       ENABLE_ASIC_STATIC_PWR_MGT_PS_ALLOCATION args;
-       int index = GetIndexIntoMasterTable(COMMAND, EnableASIC_StaticPwrMgt);
+       DYNAMIC_CLOCK_GATING_PS_ALLOCATION args;
+       int index = GetIndexIntoMasterTable(COMMAND, DynamicClockGating);
 
        args.ucEnable = enable;
 
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
new file mode 100644 (file)
index 0000000..3f557c4
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2010 Red Hat Inc.
+ * Author : Dave Airlie <airlied@redhat.com>
+ *
+ * Licensed under GPLv2
+ *
+ * ATPX support for both Intel/ATI
+ */
+#include <linux/vga_switcheroo.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <linux/pci.h>
+
+#define ATPX_VERSION 0
+#define ATPX_GPU_PWR 2
+#define ATPX_MUX_SELECT 3
+
+#define ATPX_INTEGRATED 0
+#define ATPX_DISCRETE 1
+
+#define ATPX_MUX_IGD 0
+#define ATPX_MUX_DISCRETE 1
+
+static struct radeon_atpx_priv {
+       bool atpx_detected;
+       /* handle for device - and atpx */
+       acpi_handle dhandle;
+       acpi_handle atpx_handle;
+       acpi_handle atrm_handle;
+} radeon_atpx_priv;
+
+/* retrieve the ROM in 4k blocks */
+static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
+                           int offset, int len)
+{
+       acpi_status status;
+       union acpi_object atrm_arg_elements[2], *obj;
+       struct acpi_object_list atrm_arg;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
+
+       atrm_arg.count = 2;
+       atrm_arg.pointer = &atrm_arg_elements[0];
+
+       atrm_arg_elements[0].type = ACPI_TYPE_INTEGER;
+       atrm_arg_elements[0].integer.value = offset;
+
+       atrm_arg_elements[1].type = ACPI_TYPE_INTEGER;
+       atrm_arg_elements[1].integer.value = len;
+
+       status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer);
+       if (ACPI_FAILURE(status)) {
+               printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status));
+               return -ENODEV;
+       }
+
+       obj = (union acpi_object *)buffer.pointer;
+       memcpy(bios+offset, obj->buffer.pointer, len);
+       kfree(buffer.pointer);
+       return len;
+}
+
+bool radeon_atrm_supported(struct pci_dev *pdev)
+{
+       /* get the discrete ROM only via ATRM */
+       if (!radeon_atpx_priv.atpx_detected)
+               return false;
+
+       if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
+               return false;
+       return true;
+}
+
+
+int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len)
+{
+       return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len);
+}
+
+static int radeon_atpx_get_version(acpi_handle handle)
+{
+       acpi_status status;
+       union acpi_object atpx_arg_elements[2], *obj;
+       struct acpi_object_list atpx_arg;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       atpx_arg.count = 2;
+       atpx_arg.pointer = &atpx_arg_elements[0];
+
+       atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;
+       atpx_arg_elements[0].integer.value = ATPX_VERSION;
+
+       atpx_arg_elements[1].type = ACPI_TYPE_INTEGER;
+       atpx_arg_elements[1].integer.value = ATPX_VERSION;
+
+       status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
+       if (ACPI_FAILURE(status)) {
+               printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status));
+               return -ENOSYS;
+       }
+       obj = (union acpi_object *)buffer.pointer;
+       if (obj && (obj->type == ACPI_TYPE_BUFFER))
+               printk(KERN_INFO "radeon atpx: version is %d\n", *((u8 *)(obj->buffer.pointer) + 2));
+       kfree(buffer.pointer);
+       return 0;
+}
+
+static int radeon_atpx_execute(acpi_handle handle, int cmd_id, u16 value)
+{
+       acpi_status status;
+       union acpi_object atpx_arg_elements[2];
+       struct acpi_object_list atpx_arg;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       uint8_t buf[4] = {0};
+
+       if (!handle)
+               return -EINVAL;
+
+       atpx_arg.count = 2;
+       atpx_arg.pointer = &atpx_arg_elements[0];
+
+       atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;
+       atpx_arg_elements[0].integer.value = cmd_id;
+
+       buf[2] = value & 0xff;
+       buf[3] = (value >> 8) & 0xff;
+
+       atpx_arg_elements[1].type = ACPI_TYPE_BUFFER;
+       atpx_arg_elements[1].buffer.length = 4;
+       atpx_arg_elements[1].buffer.pointer = buf;
+
+       status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
+       if (ACPI_FAILURE(status)) {
+               printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status));
+               return -ENOSYS;
+       }
+       kfree(buffer.pointer);
+
+       return 0;
+}
+
+static int radeon_atpx_set_discrete_state(acpi_handle handle, int state)
+{
+       return radeon_atpx_execute(handle, ATPX_GPU_PWR, state);
+}
+
+static int radeon_atpx_switch_mux(acpi_handle handle, int mux_id)
+{
+       return radeon_atpx_execute(handle, ATPX_MUX_SELECT, mux_id);
+}
+
+
+static int radeon_atpx_switchto(enum vga_switcheroo_client_id id)
+{
+       if (id == VGA_SWITCHEROO_IGD)
+               radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 0);
+       else
+               radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 1);
+       return 0;
+}
+
+static int radeon_atpx_power_state(enum vga_switcheroo_client_id id,
+                                  enum vga_switcheroo_state state)
+{
+       /* on w500 ACPI can't change intel gpu state */
+       if (id == VGA_SWITCHEROO_IGD)
+               return 0;
+
+       radeon_atpx_set_discrete_state(radeon_atpx_priv.atpx_handle, state);
+       return 0;
+}
+
+static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
+{
+       acpi_handle dhandle, atpx_handle, atrm_handle;
+       acpi_status status;
+
+       dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+       if (!dhandle)
+               return false;
+
+       status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
+       if (ACPI_FAILURE(status))
+               return false;
+
+       status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
+       if (ACPI_FAILURE(status))
+               return false;
+
+       radeon_atpx_priv.dhandle = dhandle;
+       radeon_atpx_priv.atpx_handle = atpx_handle;
+       radeon_atpx_priv.atrm_handle = atrm_handle;
+       return true;
+}
+
+static int radeon_atpx_init(void)
+{
+       /* set up the ATPX handle */
+
+       radeon_atpx_get_version(radeon_atpx_priv.atpx_handle);
+       return 0;
+}
+
+static int radeon_atpx_get_client_id(struct pci_dev *pdev)
+{
+       if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
+               return VGA_SWITCHEROO_IGD;
+       else
+               return VGA_SWITCHEROO_DIS;
+}
+
+static struct vga_switcheroo_handler radeon_atpx_handler = {
+       .switchto = radeon_atpx_switchto,
+       .power_state = radeon_atpx_power_state,
+       .init = radeon_atpx_init,
+       .get_client_id = radeon_atpx_get_client_id,
+};
+
+static bool radeon_atpx_detect(void)
+{
+       char acpi_method_name[255] = { 0 };
+       struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
+       struct pci_dev *pdev = NULL;
+       bool has_atpx = false;
+       int vga_count = 0;
+
+       while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
+               vga_count++;
+
+               has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true);
+       }
+
+       if (has_atpx && vga_count == 2) {
+               acpi_get_name(radeon_atpx_priv.atpx_handle, ACPI_FULL_PATHNAME, &buffer);
+               printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n",
+                      acpi_method_name);
+               radeon_atpx_priv.atpx_detected = true;
+               return true;
+       }
+       return false;
+}
+
+void radeon_register_atpx_handler(void)
+{
+       bool r;
+
+       /* detect if we have any ATPX + 2 VGA in the system */
+       r = radeon_atpx_detect();
+       if (!r)
+               return;
+
+       vga_switcheroo_register_handler(&radeon_atpx_handler);
+}
+
+void radeon_unregister_atpx_handler(void)
+{
+       vga_switcheroo_unregister_handler();
+}
index 906921740c6034153cdbf5264acf9bf5664785e8..557240460526828aeef9bc3aec07cfef26800fc0 100644 (file)
@@ -30,6 +30,7 @@
 #include "radeon.h"
 #include "atom.h"
 
+#include <linux/vga_switcheroo.h>
 /*
  * BIOS.
  */
@@ -62,7 +63,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev)
                iounmap(bios);
                return false;
        }
-       memcpy(rdev->bios, bios, size);
+       memcpy_fromio(rdev->bios, bios, size);
        iounmap(bios);
        return true;
 }
@@ -93,6 +94,38 @@ static bool radeon_read_bios(struct radeon_device *rdev)
        return true;
 }
 
+/* ATRM is used to get the BIOS on the discrete cards in
+ * dual-gpu systems.
+ */
+static bool radeon_atrm_get_bios(struct radeon_device *rdev)
+{
+       int ret;
+       int size = 64 * 1024;
+       int i;
+
+       if (!radeon_atrm_supported(rdev->pdev))
+               return false;
+
+       rdev->bios = kmalloc(size, GFP_KERNEL);
+       if (!rdev->bios) {
+               DRM_ERROR("Unable to allocate bios\n");
+               return false;
+       }
+
+       for (i = 0; i < size / ATRM_BIOS_PAGE; i++) {
+               ret = radeon_atrm_get_bios_chunk(rdev->bios,
+                                                (i * ATRM_BIOS_PAGE),
+                                                ATRM_BIOS_PAGE);
+               if (ret <= 0)
+                       break;
+       }
+
+       if (i == 0 || rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) {
+               kfree(rdev->bios);
+               return false;
+       }
+       return true;
+}
 static bool r700_read_disabled_bios(struct radeon_device *rdev)
 {
        uint32_t viph_control;
@@ -388,16 +421,16 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev)
                return legacy_read_disabled_bios(rdev);
 }
 
+
 bool radeon_get_bios(struct radeon_device *rdev)
 {
        bool r;
        uint16_t tmp;
 
-       if (rdev->flags & RADEON_IS_IGP) {
+       r = radeon_atrm_get_bios(rdev);
+       if (r == false)
                r = igp_read_bios_from_vram(rdev);
-               if (r == false)
-                       r = radeon_read_bios(rdev);
-       } else
+       if (r == false)
                r = radeon_read_bios(rdev);
        if (r == false) {
                r = radeon_read_disabled_bios(rdev);
@@ -408,6 +441,13 @@ bool radeon_get_bios(struct radeon_device *rdev)
                return false;
        }
        if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) {
+               printk("BIOS signature incorrect %x %x\n", rdev->bios[0], rdev->bios[1]);
+               goto free_bios;
+       }
+
+       tmp = RBIOS16(0x18);
+       if (RBIOS8(tmp + 0x14) != 0x0) {
+               DRM_INFO("Not an x86 BIOS ROM, not using.\n");
                goto free_bios;
        }
 
index 73c4405bf42f7af190119166a4404d8f6d31075a..f64936cc4dd9dfaffeee50d02ea529544b338abc 100644 (file)
@@ -96,6 +96,7 @@ void radeon_get_clock_info(struct drm_device *dev)
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_pll *p1pll = &rdev->clock.p1pll;
        struct radeon_pll *p2pll = &rdev->clock.p2pll;
+       struct radeon_pll *dcpll = &rdev->clock.dcpll;
        struct radeon_pll *spll = &rdev->clock.spll;
        struct radeon_pll *mpll = &rdev->clock.mpll;
        int ret;
@@ -204,6 +205,17 @@ void radeon_get_clock_info(struct drm_device *dev)
                p2pll->max_frac_feedback_div = 0;
        }
 
+       /* dcpll is DCE4 only */
+       dcpll->min_post_div = 2;
+       dcpll->max_post_div = 0x7f;
+       dcpll->min_frac_feedback_div = 0;
+       dcpll->max_frac_feedback_div = 9;
+       dcpll->min_ref_div = 2;
+       dcpll->max_ref_div = 0x3ff;
+       dcpll->min_feedback_div = 4;
+       dcpll->max_feedback_div = 0xfff;
+       dcpll->best_vco = 0;
+
        p1pll->min_ref_div = 2;
        p1pll->max_ref_div = 0x3ff;
        p1pll->min_feedback_div = 4;
@@ -846,8 +858,10 @@ int radeon_static_clocks_init(struct drm_device *dev)
        /* XXX make sure engine is idle */
 
        if (radeon_dynclks != -1) {
-               if (radeon_dynclks)
-                       radeon_set_clock_gating(rdev, 1);
+               if (radeon_dynclks) {
+                       if (rdev->asic->set_clock_gating)
+                               radeon_set_clock_gating(rdev, 1);
+               }
        }
        radeon_apply_clock_quirks(rdev);
        return 0;
index 22d476160d5293ec74e38a44e14630eea5922c9b..e9ea38ece37558a6bf123b18a982b06ec252872a 100644 (file)
@@ -150,6 +150,9 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
        int rev;
        uint16_t offset = 0, check_offset;
 
+       if (!rdev->bios)
+               return 0;
+
        switch (table) {
                /* absolute offset tables */
        case COMBIOS_ASIC_INIT_1_TABLE:
@@ -443,6 +446,39 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
 
 }
 
+bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
+{
+       int edid_info;
+       struct edid *edid;
+       edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE);
+       if (!edid_info)
+               return false;
+
+       edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
+                      GFP_KERNEL);
+       if (edid == NULL)
+               return false;
+
+       memcpy((unsigned char *)edid,
+              (unsigned char *)(rdev->bios + edid_info), EDID_LENGTH);
+
+       if (!drm_edid_is_valid(edid)) {
+               kfree(edid);
+               return false;
+       }
+
+       rdev->mode_info.bios_hardcoded_edid = edid;
+       return true;
+}
+
+struct edid *
+radeon_combios_get_hardcoded_edid(struct radeon_device *rdev)
+{
+       if (rdev->mode_info.bios_hardcoded_edid)
+               return rdev->mode_info.bios_hardcoded_edid;
+       return NULL;
+}
+
 static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
                                                       int ddc_line)
 {
@@ -486,9 +522,65 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
                i2c.y_data_reg = ddc_line;
        }
 
-       if (rdev->family < CHIP_R200)
-               i2c.hw_capable = false;
-       else {
+       switch (rdev->family) {
+       case CHIP_R100:
+       case CHIP_RV100:
+       case CHIP_RS100:
+       case CHIP_RV200:
+       case CHIP_RS200:
+       case CHIP_RS300:
+               switch (ddc_line) {
+               case RADEON_GPIO_DVI_DDC:
+                       /* in theory this should be hw capable,
+                        * but it doesn't seem to work
+                        */
+                       i2c.hw_capable = false;
+                       break;
+               default:
+                       i2c.hw_capable = false;
+                       break;
+               }
+               break;
+       case CHIP_R200:
+               switch (ddc_line) {
+               case RADEON_GPIO_DVI_DDC:
+               case RADEON_GPIO_MONID:
+                       i2c.hw_capable = true;
+                       break;
+               default:
+                       i2c.hw_capable = false;
+                       break;
+               }
+               break;
+       case CHIP_RV250:
+       case CHIP_RV280:
+               switch (ddc_line) {
+               case RADEON_GPIO_VGA_DDC:
+               case RADEON_GPIO_DVI_DDC:
+               case RADEON_GPIO_CRT2_DDC:
+                       i2c.hw_capable = true;
+                       break;
+               default:
+                       i2c.hw_capable = false;
+                       break;
+               }
+               break;
+       case CHIP_R300:
+       case CHIP_R350:
+               switch (ddc_line) {
+               case RADEON_GPIO_VGA_DDC:
+               case RADEON_GPIO_DVI_DDC:
+                       i2c.hw_capable = true;
+                       break;
+               default:
+                       i2c.hw_capable = false;
+                       break;
+               }
+               break;
+       case CHIP_RV350:
+       case CHIP_RV380:
+       case CHIP_RS400:
+       case CHIP_RS480:
                switch (ddc_line) {
                case RADEON_GPIO_VGA_DDC:
                case RADEON_GPIO_DVI_DDC:
@@ -504,9 +596,14 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
                        i2c.hw_capable = false;
                        break;
                }
+               break;
+       default:
+               i2c.hw_capable = false;
+               break;
        }
        i2c.mm_i2c = false;
        i2c.i2c_id = 0;
+       i2c.hpd_id = 0;
 
        if (ddc_line)
                i2c.valid = true;
@@ -527,9 +624,6 @@ bool radeon_combios_get_clock_info(struct drm_device *dev)
        int8_t rev;
        uint16_t sclk, mclk;
 
-       if (rdev->bios == NULL)
-               return false;
-
        pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE);
        if (pll_info) {
                rev = RBIOS8(pll_info);
@@ -654,9 +748,6 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
        if (!p_dac)
                return NULL;
 
-       if (rdev->bios == NULL)
-               goto out;
-
        /* check CRT table */
        dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
        if (dac_info) {
@@ -673,7 +764,6 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
                found = 1;
        }
 
-out:
        if (!found) /* fallback to defaults */
                radeon_legacy_get_primary_dac_info_from_table(rdev, p_dac);
 
@@ -687,9 +777,6 @@ radeon_combios_get_tv_info(struct radeon_device *rdev)
        uint16_t tv_info;
        enum radeon_tv_std tv_std = TV_STD_NTSC;
 
-       if (rdev->bios == NULL)
-               return tv_std;
-
        tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE);
        if (tv_info) {
                if (RBIOS8(tv_info + 6) == 'T') {
@@ -793,9 +880,6 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
        if (!tv_dac)
                return NULL;
 
-       if (rdev->bios == NULL)
-               goto out;
-
        /* first check TV table */
        dac_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE);
        if (dac_info) {
@@ -857,7 +941,6 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
                }
        }
 
-out:
        if (!found) /* fallback to defaults */
                radeon_legacy_get_tv_dac_info_from_table(rdev, tv_dac);
 
@@ -945,11 +1028,6 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder
        int tmp, i;
        struct radeon_encoder_lvds *lvds = NULL;
 
-       if (rdev->bios == NULL) {
-               lvds = radeon_legacy_get_lvds_info_from_regs(rdev);
-               goto out;
-       }
-
        lcd_info = combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE);
 
        if (lcd_info) {
@@ -1050,7 +1128,7 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder
                DRM_INFO("No panel info found in BIOS\n");
                lvds = radeon_legacy_get_lvds_info_from_regs(rdev);
        }
-out:
+
        if (lvds)
                encoder->native_mode = lvds->native_mode;
        return lvds;
@@ -1102,9 +1180,6 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
        int i, n;
        uint8_t ver;
 
-       if (rdev->bios == NULL)
-               return false;
-
        tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE);
 
        if (tmds_info) {
@@ -1184,9 +1259,6 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder
        enum radeon_combios_ddc gpio;
        struct radeon_i2c_bus_rec i2c_bus;
 
-       if (rdev->bios == NULL)
-               return false;
-
        tmds->i2c_bus = NULL;
        if (rdev->flags & RADEON_IS_IGP) {
                offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
@@ -1253,7 +1325,10 @@ bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder
                                tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
                                break;
                        case DDC_LCD: /* MM i2c */
-                               DRM_ERROR("MM i2c requires hw i2c engine\n");
+                               i2c_bus.valid = true;
+                               i2c_bus.hw_capable = true;
+                               i2c_bus.mm_i2c = true;
+                               tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
                                break;
                        default:
                                DRM_ERROR("Unsupported gpio %d\n", gpio);
@@ -1909,9 +1984,6 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
        struct radeon_i2c_bus_rec ddc_i2c;
        struct radeon_hpd hpd;
 
-       if (rdev->bios == NULL)
-               return false;
-
        conn_info = combios_get_table_offset(dev, COMBIOS_CONNECTOR_INFO_TABLE);
        if (conn_info) {
                for (i = 0; i < 4; i++) {
@@ -2278,6 +2350,115 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
        return true;
 }
 
+void radeon_combios_get_power_modes(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       u16 offset, misc, misc2 = 0;
+       u8 rev, blocks, tmp;
+       int state_index = 0;
+
+       rdev->pm.default_power_state = NULL;
+
+       if (rdev->flags & RADEON_IS_MOBILITY) {
+               offset = combios_get_table_offset(dev, COMBIOS_POWERPLAY_INFO_TABLE);
+               if (offset) {
+                       rev = RBIOS8(offset);
+                       blocks = RBIOS8(offset + 0x2);
+                       /* power mode 0 tends to be the only valid one */
+                       rdev->pm.power_state[state_index].num_clock_modes = 1;
+                       rdev->pm.power_state[state_index].clock_info[0].mclk = RBIOS32(offset + 0x5 + 0x2);
+                       rdev->pm.power_state[state_index].clock_info[0].sclk = RBIOS32(offset + 0x5 + 0x6);
+                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk == 0) ||
+                           (rdev->pm.power_state[state_index].clock_info[0].sclk == 0))
+                               goto default_mode;
+                       /* skip overclock modes for now */
+                       if ((rdev->pm.power_state[state_index].clock_info[0].mclk >
+                            rdev->clock.default_mclk + RADEON_MODE_OVERCLOCK_MARGIN) ||
+                           (rdev->pm.power_state[state_index].clock_info[0].sclk >
+                            rdev->clock.default_sclk + RADEON_MODE_OVERCLOCK_MARGIN))
+                               goto default_mode;
+                       rdev->pm.power_state[state_index].type =
+                               POWER_STATE_TYPE_BATTERY;
+                       misc = RBIOS16(offset + 0x5 + 0x0);
+                       if (rev > 4)
+                               misc2 = RBIOS16(offset + 0x5 + 0xe);
+                       if (misc & 0x4) {
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_GPIO;
+                               if (misc & 0x8)
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                               true;
+                               else
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.active_high =
+                                               false;
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.valid = true;
+                               if (rev < 6) {
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.reg =
+                                               RBIOS16(offset + 0x5 + 0xb) * 4;
+                                       tmp = RBIOS8(offset + 0x5 + 0xd);
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.mask = (1 << tmp);
+                               } else {
+                                       u8 entries = RBIOS8(offset + 0x5 + 0xb);
+                                       u16 voltage_table_offset = RBIOS16(offset + 0x5 + 0xc);
+                                       if (entries && voltage_table_offset) {
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.reg =
+                                                       RBIOS16(voltage_table_offset) * 4;
+                                               tmp = RBIOS8(voltage_table_offset + 0x2);
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.mask = (1 << tmp);
+                                       } else
+                                               rdev->pm.power_state[state_index].clock_info[0].voltage.gpio.valid = false;
+                               }
+                               switch ((misc2 & 0x700) >> 8) {
+                               case 0:
+                               default:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 0;
+                                       break;
+                               case 1:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 33;
+                                       break;
+                               case 2:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 66;
+                                       break;
+                               case 3:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 99;
+                                       break;
+                               case 4:
+                                       rdev->pm.power_state[state_index].clock_info[0].voltage.delay = 132;
+                                       break;
+                               }
+                       } else
+                               rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+                       if (rev > 6)
+                               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes =
+                                       RBIOS8(offset + 0x5 + 0x10);
+                       state_index++;
+               } else {
+                       /* XXX figure out some good default low power mode for mobility cards w/out power tables */
+               }
+       } else {
+               /* XXX figure out some good default low power mode for desktop cards */
+       }
+
+default_mode:
+       /* add the default mode */
+       rdev->pm.power_state[state_index].type =
+               POWER_STATE_TYPE_DEFAULT;
+       rdev->pm.power_state[state_index].num_clock_modes = 1;
+       rdev->pm.power_state[state_index].clock_info[0].mclk = rdev->clock.default_mclk;
+       rdev->pm.power_state[state_index].clock_info[0].sclk = rdev->clock.default_sclk;
+       rdev->pm.power_state[state_index].default_clock_mode = &rdev->pm.power_state[state_index].clock_info[0];
+       rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
+       if (rdev->asic->get_pcie_lanes)
+               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = radeon_get_pcie_lanes(rdev);
+       else
+               rdev->pm.power_state[state_index].non_clock_info.pcie_lanes = 16;
+       rdev->pm.default_power_state = &rdev->pm.power_state[state_index];
+       rdev->pm.num_power_states = state_index + 1;
+
+       rdev->pm.current_power_state = rdev->pm.default_power_state;
+       rdev->pm.current_clock_mode =
+               rdev->pm.default_power_state->default_clock_mode;
+}
+
 void radeon_external_tmds_setup(struct drm_encoder *encoder)
 {
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
@@ -2289,23 +2470,21 @@ void radeon_external_tmds_setup(struct drm_encoder *encoder)
        switch (tmds->dvo_chip) {
        case DVO_SIL164:
                /* sil 164 */
-               radeon_i2c_do_lock(tmds->i2c_bus, 1);
-               radeon_i2c_sw_put_byte(tmds->i2c_bus,
-                                      tmds->slave_addr,
-                                      0x08, 0x30);
-               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+               radeon_i2c_put_byte(tmds->i2c_bus,
+                                   tmds->slave_addr,
+                                   0x08, 0x30);
+               radeon_i2c_put_byte(tmds->i2c_bus,
                                       tmds->slave_addr,
                                       0x09, 0x00);
-               radeon_i2c_sw_put_byte(tmds->i2c_bus,
-                                      tmds->slave_addr,
-                                      0x0a, 0x90);
-               radeon_i2c_sw_put_byte(tmds->i2c_bus,
-                                      tmds->slave_addr,
-                                      0x0c, 0x89);
-               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+               radeon_i2c_put_byte(tmds->i2c_bus,
+                                   tmds->slave_addr,
+                                   0x0a, 0x90);
+               radeon_i2c_put_byte(tmds->i2c_bus,
+                                   tmds->slave_addr,
+                                   0x0c, 0x89);
+               radeon_i2c_put_byte(tmds->i2c_bus,
                                       tmds->slave_addr,
                                       0x08, 0x3b);
-               radeon_i2c_do_lock(tmds->i2c_bus, 0);
                break;
        case DVO_SIL1178:
                /* sil 1178 - untested */
@@ -2338,9 +2517,6 @@ bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder)
        uint32_t reg, val, and_mask, or_mask;
        struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
 
-       if (rdev->bios == NULL)
-               return false;
-
        if (!tmds)
                return false;
 
@@ -2390,11 +2566,9 @@ bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder)
                                                index++;
                                                val = RBIOS8(index);
                                                index++;
-                                               radeon_i2c_do_lock(tmds->i2c_bus, 1);
-                                               radeon_i2c_sw_put_byte(tmds->i2c_bus,
-                                                                      slave_addr,
-                                                                      reg, val);
-                                               radeon_i2c_do_lock(tmds->i2c_bus, 0);
+                                               radeon_i2c_put_byte(tmds->i2c_bus,
+                                                                   slave_addr,
+                                                                   reg, val);
                                                break;
                                        default:
                                                DRM_ERROR("Unknown id %d\n", id >> 13);
@@ -2447,11 +2621,9 @@ bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder)
                                        reg = id & 0x1fff;
                                        val = RBIOS8(index);
                                        index += 1;
-                                       radeon_i2c_do_lock(tmds->i2c_bus, 1);
-                                       radeon_i2c_sw_put_byte(tmds->i2c_bus,
-                                                              tmds->slave_addr,
-                                                              reg, val);
-                                       radeon_i2c_do_lock(tmds->i2c_bus, 0);
+                                       radeon_i2c_put_byte(tmds->i2c_bus,
+                                                           tmds->slave_addr,
+                                                           reg, val);
                                        break;
                                default:
                                        DRM_ERROR("Unknown id %d\n", id >> 13);
index 65f81942f3994684e829b79bec2a7a7b1d9422b1..ee0083f982d8ba2a926688e083ea930716e0de91 100644 (file)
@@ -479,10 +479,8 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec
                ret = connector_status_connected;
        else {
                if (radeon_connector->ddc_bus) {
-                       radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                        radeon_connector->edid = drm_get_edid(&radeon_connector->base,
                                                              &radeon_connector->ddc_bus->adapter);
-                       radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
                        if (radeon_connector->edid)
                                ret = connector_status_connected;
                }
@@ -587,19 +585,14 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
        if (!encoder)
                ret = connector_status_disconnected;
 
-       if (radeon_connector->ddc_bus) {
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+       if (radeon_connector->ddc_bus)
                dret = radeon_ddc_probe(radeon_connector);
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
-       }
        if (dret) {
                if (radeon_connector->edid) {
                        kfree(radeon_connector->edid);
                        radeon_connector->edid = NULL;
                }
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
 
                if (!radeon_connector->edid) {
                        DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
@@ -744,19 +737,14 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
        enum drm_connector_status ret = connector_status_disconnected;
        bool dret = false;
 
-       if (radeon_connector->ddc_bus) {
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+       if (radeon_connector->ddc_bus)
                dret = radeon_ddc_probe(radeon_connector);
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
-       }
        if (dret) {
                if (radeon_connector->edid) {
                        kfree(radeon_connector->edid);
                        radeon_connector->edid = NULL;
                }
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
 
                if (!radeon_connector->edid) {
                        DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
@@ -952,7 +940,7 @@ static void radeon_dp_connector_destroy(struct drm_connector *connector)
        if (radeon_connector->edid)
                kfree(radeon_connector->edid);
        if (radeon_dig_connector->dp_i2c_bus)
-               radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus);
+               radeon_i2c_destroy_dp(radeon_dig_connector->dp_i2c_bus);
        kfree(radeon_connector->con_priv);
        drm_sysfs_connector_remove(connector);
        drm_connector_cleanup(connector);
@@ -988,12 +976,10 @@ static enum drm_connector_status radeon_dp_detect(struct drm_connector *connecto
                        ret = connector_status_connected;
                }
        } else {
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                if (radeon_ddc_probe(radeon_connector)) {
                        radeon_dig_connector->dp_sink_type = sink_type;
                        ret = connector_status_connected;
                }
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        }
 
        return ret;
index 06123ba31d31ff564ea4224a2f0de56e87cdbf53..dc6eba6b96dd1f5476cd3f6bda470e0cda6b245b 100644 (file)
@@ -1644,6 +1644,7 @@ static int radeon_do_resume_cp(struct drm_device *dev, struct drm_file *file_pri
        radeon_cp_load_microcode(dev_priv);
        radeon_cp_init_ring_buffer(dev, dev_priv, file_priv);
 
+       dev_priv->have_z_offset = 0;
        radeon_do_engine_reset(dev);
        radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
 
index e9d085021c1f08d2fb7b0edc40450eb9fa7580a2..70ba02ed77237ba31299a7cbc56694dc3e62d57a 100644 (file)
@@ -194,11 +194,8 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
        }
        radeon_bo_list_unreserve(&parser->validated);
        for (i = 0; i < parser->nrelocs; i++) {
-               if (parser->relocs[i].gobj) {
-                       mutex_lock(&parser->rdev->ddev->struct_mutex);
-                       drm_gem_object_unreference(parser->relocs[i].gobj);
-                       mutex_unlock(&parser->rdev->ddev->struct_mutex);
-               }
+               if (parser->relocs[i].gobj)
+                       drm_gem_object_unreference_unlocked(parser->relocs[i].gobj);
        }
        kfree(parser->track);
        kfree(parser->relocs);
index 28772a37009c189a04f94bae7d891087c1e53dfc..b7023fff89eb258b00d6aee84e7632954fe00384 100644 (file)
@@ -36,7 +36,14 @@ static void radeon_lock_cursor(struct drm_crtc *crtc, bool lock)
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        uint32_t cur_lock;
 
-       if (ASIC_IS_AVIVO(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               cur_lock = RREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset);
+               if (lock)
+                       cur_lock |= EVERGREEN_CURSOR_UPDATE_LOCK;
+               else
+                       cur_lock &= ~EVERGREEN_CURSOR_UPDATE_LOCK;
+               WREG32(EVERGREEN_CUR_UPDATE + radeon_crtc->crtc_offset, cur_lock);
+       } else if (ASIC_IS_AVIVO(rdev)) {
                cur_lock = RREG32(AVIVO_D1CUR_UPDATE + radeon_crtc->crtc_offset);
                if (lock)
                        cur_lock |= AVIVO_D1CURSOR_UPDATE_LOCK;
@@ -58,7 +65,10 @@ static void radeon_hide_cursor(struct drm_crtc *crtc)
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct radeon_device *rdev = crtc->dev->dev_private;
 
-       if (ASIC_IS_AVIVO(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
+               WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT));
+       } else if (ASIC_IS_AVIVO(rdev)) {
                WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
                WREG32(RADEON_MM_DATA, (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
        } else {
@@ -81,10 +91,14 @@ static void radeon_show_cursor(struct drm_crtc *crtc)
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct radeon_device *rdev = crtc->dev->dev_private;
 
-       if (ASIC_IS_AVIVO(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
+               WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_EN |
+                      EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT));
+       } else if (ASIC_IS_AVIVO(rdev)) {
                WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
                WREG32(RADEON_MM_DATA, AVIVO_D1CURSOR_EN |
-                            (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
+                      (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
        } else {
                switch (radeon_crtc->crtc_id) {
                case 0:
@@ -109,7 +123,10 @@ static void radeon_set_cursor(struct drm_crtc *crtc, struct drm_gem_object *obj,
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct radeon_device *rdev = crtc->dev->dev_private;
 
-       if (ASIC_IS_AVIVO(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 0);
+               WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + radeon_crtc->crtc_offset, gpu_addr);
+       } else if (ASIC_IS_AVIVO(rdev)) {
                if (rdev->family >= CHIP_RV770) {
                        if (radeon_crtc->crtc_id)
                                WREG32(R700_D2CUR_SURFACE_ADDRESS_HIGH, 0);
@@ -169,17 +186,13 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
 unpin:
        if (radeon_crtc->cursor_bo) {
                radeon_gem_object_unpin(radeon_crtc->cursor_bo);
-               mutex_lock(&crtc->dev->struct_mutex);
-               drm_gem_object_unreference(radeon_crtc->cursor_bo);
-               mutex_unlock(&crtc->dev->struct_mutex);
+               drm_gem_object_unreference_unlocked(radeon_crtc->cursor_bo);
        }
 
        radeon_crtc->cursor_bo = obj;
        return 0;
 fail:
-       mutex_lock(&crtc->dev->struct_mutex);
-       drm_gem_object_unreference(obj);
-       mutex_unlock(&crtc->dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(obj);
 
        return 0;
 }
@@ -201,7 +214,20 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc,
                yorigin = CURSOR_HEIGHT - 1;
 
        radeon_lock_cursor(crtc, true);
-       if (ASIC_IS_AVIVO(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               /* cursors are offset into the total surface */
+               x += crtc->x;
+               y += crtc->y;
+               DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
+
+               /* XXX: check if evergreen has the same issues as avivo chips */
+               WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset,
+                      ((xorigin ? 0 : x) << 16) |
+                      (yorigin ? 0 : y));
+               WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
+               WREG32(EVERGREEN_CUR_SIZE + radeon_crtc->crtc_offset,
+                      ((radeon_crtc->cursor_width - 1) << 16) | (radeon_crtc->cursor_height - 1));
+       } else if (ASIC_IS_AVIVO(rdev)) {
                int w = radeon_crtc->cursor_width;
                int i = 0;
                struct drm_crtc *crtc_p;
index 768b1509fa032b1a90ec9a38bab111672ebfbd4f..e28e4ed5f720d5340c1e23595ca099d1559273c0 100644 (file)
@@ -30,6 +30,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/radeon_drm.h>
 #include <linux/vgaarb.h>
+#include <linux/vga_switcheroo.h>
 #include "radeon_reg.h"
 #include "radeon.h"
 #include "radeon_asic.h"
@@ -100,80 +101,103 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg)
        }
 }
 
-/*
- * MC common functions
+/**
+ * radeon_vram_location - try to find VRAM location
+ * @rdev: radeon device structure holding all necessary informations
+ * @mc: memory controller structure holding memory informations
+ * @base: base address at which to put VRAM
+ *
+ * Function will place try to place VRAM at base address provided
+ * as parameter (which is so far either PCI aperture address or
+ * for IGP TOM base address).
+ *
+ * If there is not enough space to fit the unvisible VRAM in the 32bits
+ * address space then we limit the VRAM size to the aperture.
+ *
+ * If we are using AGP and if the AGP aperture doesn't allow us to have
+ * room for all the VRAM than we restrict the VRAM to the PCI aperture
+ * size and print a warning.
+ *
+ * This function will never fails, worst case are limiting VRAM.
+ *
+ * Note: GTT start, end, size should be initialized before calling this
+ * function on AGP platform.
+ *
+ * Note: We don't explictly enforce VRAM start to be aligned on VRAM size,
+ * this shouldn't be a problem as we are using the PCI aperture as a reference.
+ * Otherwise this would be needed for rv280, all r3xx, and all r4xx, but
+ * not IGP.
+ *
+ * Note: we use mc_vram_size as on some board we need to program the mc to
+ * cover the whole aperture even if VRAM size is inferior to aperture size
+ * Novell bug 204882 + along with lots of ubuntu ones
+ *
+ * Note: when limiting vram it's safe to overwritte real_vram_size because
+ * we are not in case where real_vram_size is inferior to mc_vram_size (ie
+ * note afected by bogus hw of Novell bug 204882 + along with lots of ubuntu
+ * ones)
+ *
+ * Note: IGP TOM addr should be the same as the aperture addr, we don't
+ * explicitly check for that thought.
+ *
+ * FIXME: when reducing VRAM size align new size on power of 2.
+ */
+void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base)
+{
+       mc->vram_start = base;
+       if (mc->mc_vram_size > (0xFFFFFFFF - base + 1)) {
+               dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n");
+               mc->real_vram_size = mc->aper_size;
+               mc->mc_vram_size = mc->aper_size;
+       }
+       mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;
+       if (rdev->flags & RADEON_IS_AGP && mc->vram_end > mc->gtt_start && mc->vram_end <= mc->gtt_end) {
+               dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n");
+               mc->real_vram_size = mc->aper_size;
+               mc->mc_vram_size = mc->aper_size;
+       }
+       mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;
+       dev_info(rdev->dev, "VRAM: %lluM 0x%08llX - 0x%08llX (%lluM used)\n",
+                       mc->mc_vram_size >> 20, mc->vram_start,
+                       mc->vram_end, mc->real_vram_size >> 20);
+}
+
+/**
+ * radeon_gtt_location - try to find GTT location
+ * @rdev: radeon device structure holding all necessary informations
+ * @mc: memory controller structure holding memory informations
+ *
+ * Function will place try to place GTT before or after VRAM.
+ *
+ * If GTT size is bigger than space left then we ajust GTT size.
+ * Thus function will never fails.
+ *
+ * FIXME: when reducing GTT size align new size on power of 2.
  */
-int radeon_mc_setup(struct radeon_device *rdev)
+void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc)
 {
-       uint32_t tmp;
+       u64 size_af, size_bf;
 
-       /* Some chips have an "issue" with the memory controller, the
-        * location must be aligned to the size. We just align it down,
-        * too bad if we walk over the top of system memory, we don't
-        * use DMA without a remapped anyway.
-        * Affected chips are rv280, all r3xx, and all r4xx, but not IGP
-        */
-       /* FGLRX seems to setup like this, VRAM a 0, then GART.
-        */
-       /*
-        * Note: from R6xx the address space is 40bits but here we only
-        * use 32bits (still have to see a card which would exhaust 4G
-        * address space).
-        */
-       if (rdev->mc.vram_location != 0xFFFFFFFFUL) {
-               /* vram location was already setup try to put gtt after
-                * if it fits */
-               tmp = rdev->mc.vram_location + rdev->mc.mc_vram_size;
-               tmp = (tmp + rdev->mc.gtt_size - 1) & ~(rdev->mc.gtt_size - 1);
-               if ((0xFFFFFFFFUL - tmp) >= rdev->mc.gtt_size) {
-                       rdev->mc.gtt_location = tmp;
-               } else {
-                       if (rdev->mc.gtt_size >= rdev->mc.vram_location) {
-                               printk(KERN_ERR "[drm] GTT too big to fit "
-                                      "before or after vram location.\n");
-                               return -EINVAL;
-                       }
-                       rdev->mc.gtt_location = 0;
-               }
-       } else if (rdev->mc.gtt_location != 0xFFFFFFFFUL) {
-               /* gtt location was already setup try to put vram before
-                * if it fits */
-               if (rdev->mc.mc_vram_size < rdev->mc.gtt_location) {
-                       rdev->mc.vram_location = 0;
-               } else {
-                       tmp = rdev->mc.gtt_location + rdev->mc.gtt_size;
-                       tmp += (rdev->mc.mc_vram_size - 1);
-                       tmp &= ~(rdev->mc.mc_vram_size - 1);
-                       if ((0xFFFFFFFFUL - tmp) >= rdev->mc.mc_vram_size) {
-                               rdev->mc.vram_location = tmp;
-                       } else {
-                               printk(KERN_ERR "[drm] vram too big to fit "
-                                      "before or after GTT location.\n");
-                               return -EINVAL;
-                       }
+       size_af = 0xFFFFFFFF - mc->vram_end;
+       size_bf = mc->vram_start;
+       if (size_bf > size_af) {
+               if (mc->gtt_size > size_bf) {
+                       dev_warn(rdev->dev, "limiting GTT\n");
+                       mc->gtt_size = size_bf;
                }
+               mc->gtt_start = mc->vram_start - mc->gtt_size;
        } else {
-               rdev->mc.vram_location = 0;
-               tmp = rdev->mc.mc_vram_size;
-               tmp = (tmp + rdev->mc.gtt_size - 1) & ~(rdev->mc.gtt_size - 1);
-               rdev->mc.gtt_location = tmp;
-       }
-       rdev->mc.vram_start = rdev->mc.vram_location;
-       rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1;
-       rdev->mc.gtt_start = rdev->mc.gtt_location;
-       rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
-       DRM_INFO("radeon: VRAM %uM\n", (unsigned)(rdev->mc.mc_vram_size >> 20));
-       DRM_INFO("radeon: VRAM from 0x%08X to 0x%08X\n",
-                (unsigned)rdev->mc.vram_location,
-                (unsigned)(rdev->mc.vram_location + rdev->mc.mc_vram_size - 1));
-       DRM_INFO("radeon: GTT %uM\n", (unsigned)(rdev->mc.gtt_size >> 20));
-       DRM_INFO("radeon: GTT from 0x%08X to 0x%08X\n",
-                (unsigned)rdev->mc.gtt_location,
-                (unsigned)(rdev->mc.gtt_location + rdev->mc.gtt_size - 1));
-       return 0;
+               if (mc->gtt_size > size_af) {
+                       dev_warn(rdev->dev, "limiting GTT\n");
+                       mc->gtt_size = size_af;
+               }
+               mc->gtt_start = mc->vram_end + 1;
+       }
+       mc->gtt_end = mc->gtt_start + mc->gtt_size - 1;
+       dev_info(rdev->dev, "GTT: %lluM 0x%08llX - 0x%08llX\n",
+                       mc->gtt_size >> 20, mc->gtt_start, mc->gtt_end);
 }
 
-
 /*
  * GPU helpers function.
  */
@@ -182,7 +206,16 @@ bool radeon_card_posted(struct radeon_device *rdev)
        uint32_t reg;
 
        /* first check CRTCs */
-       if (ASIC_IS_AVIVO(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               reg = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) |
+                       RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) |
+                       RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET) |
+                       RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET) |
+                       RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET) |
+                       RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
+               if (reg & EVERGREEN_CRTC_MASTER_EN)
+                       return true;
+       } else if (ASIC_IS_AVIVO(rdev)) {
                reg = RREG32(AVIVO_D1CRTC_CONTROL) |
                      RREG32(AVIVO_D2CRTC_CONTROL);
                if (reg & AVIVO_CRTC_EN) {
@@ -229,6 +262,8 @@ bool radeon_boot_test_post_card(struct radeon_device *rdev)
 
 int radeon_dummy_page_init(struct radeon_device *rdev)
 {
+       if (rdev->dummy_page.page)
+               return 0;
        rdev->dummy_page.page = alloc_page(GFP_DMA32 | GFP_KERNEL | __GFP_ZERO);
        if (rdev->dummy_page.page == NULL)
                return -ENOMEM;
@@ -310,7 +345,7 @@ void radeon_register_accessor_init(struct radeon_device *rdev)
                rdev->mc_rreg = &rs600_mc_rreg;
                rdev->mc_wreg = &rs600_mc_wreg;
        }
-       if (rdev->family >= CHIP_R600) {
+       if ((rdev->family >= CHIP_R600) && (rdev->family <= CHIP_RV740)) {
                rdev->pciep_rreg = &r600_pciep_rreg;
                rdev->pciep_wreg = &r600_pciep_wreg;
        }
@@ -329,21 +364,22 @@ int radeon_asic_init(struct radeon_device *rdev)
        case CHIP_RS100:
        case CHIP_RV200:
        case CHIP_RS200:
+               rdev->asic = &r100_asic;
+               break;
        case CHIP_R200:
        case CHIP_RV250:
        case CHIP_RS300:
        case CHIP_RV280:
-               rdev->asic = &r100_asic;
+               rdev->asic = &r200_asic;
                break;
        case CHIP_R300:
        case CHIP_R350:
        case CHIP_RV350:
        case CHIP_RV380:
-               rdev->asic = &r300_asic;
-               if (rdev->flags & RADEON_IS_PCIE) {
-                       rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush;
-                       rdev->asic->gart_set_page = &rv370_pcie_gart_set_page;
-               }
+               if (rdev->flags & RADEON_IS_PCIE)
+                       rdev->asic = &r300_asic_pcie;
+               else
+                       rdev->asic = &r300_asic;
                break;
        case CHIP_R420:
        case CHIP_R423:
@@ -387,6 +423,13 @@ int radeon_asic_init(struct radeon_device *rdev)
        case CHIP_RV740:
                rdev->asic = &rv770_asic;
                break;
+       case CHIP_CEDAR:
+       case CHIP_REDWOOD:
+       case CHIP_JUNIPER:
+       case CHIP_CYPRESS:
+       case CHIP_HEMLOCK:
+               rdev->asic = &evergreen_asic;
+               break;
        default:
                /* FIXME: not supported yet */
                return -EINVAL;
@@ -613,6 +656,36 @@ void radeon_check_arguments(struct radeon_device *rdev)
        }
 }
 
+static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       struct radeon_device *rdev = dev->dev_private;
+       pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+       if (state == VGA_SWITCHEROO_ON) {
+               printk(KERN_INFO "radeon: switched on\n");
+               /* don't suspend or resume card normally */
+               rdev->powered_down = false;
+               radeon_resume_kms(dev);
+       } else {
+               printk(KERN_INFO "radeon: switched off\n");
+               radeon_suspend_kms(dev, pmm);
+               /* don't suspend or resume card normally */
+               rdev->powered_down = true;
+       }
+}
+
+static bool radeon_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
+}
+
+
 int radeon_device_init(struct radeon_device *rdev,
                       struct drm_device *ddev,
                       struct pci_dev *pdev,
@@ -638,11 +711,14 @@ int radeon_device_init(struct radeon_device *rdev,
        mutex_init(&rdev->cs_mutex);
        mutex_init(&rdev->ib_pool.mutex);
        mutex_init(&rdev->cp.mutex);
+       mutex_init(&rdev->dc_hw_i2c_mutex);
        if (rdev->family >= CHIP_R600)
                spin_lock_init(&rdev->ih.lock);
        mutex_init(&rdev->gem.mutex);
+       mutex_init(&rdev->pm.mutex);
        rwlock_init(&rdev->fence_drv.lock);
        INIT_LIST_HEAD(&rdev->gem.objects);
+       init_waitqueue_head(&rdev->irq.vblank_queue);
 
        /* setup workqueue */
        rdev->wq = create_workqueue("radeon");
@@ -692,6 +768,9 @@ int radeon_device_init(struct radeon_device *rdev,
        /* this will fail for cards that aren't VGA class devices, just
         * ignore it */
        vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
+       vga_switcheroo_register_client(rdev->pdev,
+                                      radeon_switcheroo_set_state,
+                                      radeon_switcheroo_can_switch);
 
        r = radeon_init(rdev);
        if (r)
@@ -723,6 +802,7 @@ void radeon_device_fini(struct radeon_device *rdev)
        rdev->shutdown = true;
        radeon_fini(rdev);
        destroy_workqueue(rdev->wq);
+       vga_switcheroo_unregister_client(rdev->pdev);
        vga_client_register(rdev->pdev, NULL, NULL, NULL);
        iounmap(rdev->rmmio);
        rdev->rmmio = NULL;
@@ -746,6 +826,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
        }
        rdev = dev->dev_private;
 
+       if (rdev->powered_down)
+               return 0;
        /* unpin the front buffers */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb);
@@ -791,6 +873,9 @@ int radeon_resume_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
 
+       if (rdev->powered_down)
+               return 0;
+
        acquire_console_sem();
        pci_set_power_state(dev->pdev, PCI_D0);
        pci_restore_state(dev->pdev);
index 7e17a362b54baab34cf1b8a41e60a4a5c3c6865b..ba8d806dcf3939fe91285820793e7b733ffb0541 100644 (file)
@@ -68,6 +68,36 @@ static void avivo_crtc_load_lut(struct drm_crtc *crtc)
        WREG32(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id);
 }
 
+static void evergreen_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       int i;
+
+       DRM_DEBUG("%d\n", radeon_crtc->crtc_id);
+       WREG32(EVERGREEN_DC_LUT_CONTROL + radeon_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff);
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff);
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff);
+
+       WREG32(EVERGREEN_DC_LUT_RW_MODE, radeon_crtc->crtc_id);
+       WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK, 0x00000007);
+
+       WREG32(EVERGREEN_DC_LUT_RW_INDEX, 0);
+       for (i = 0; i < 256; i++) {
+               WREG32(EVERGREEN_DC_LUT_30_COLOR,
+                      (radeon_crtc->lut_r[i] << 20) |
+                      (radeon_crtc->lut_g[i] << 10) |
+                      (radeon_crtc->lut_b[i] << 0));
+       }
+}
+
 static void legacy_crtc_load_lut(struct drm_crtc *crtc)
 {
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -100,7 +130,9 @@ void radeon_crtc_load_lut(struct drm_crtc *crtc)
        if (!crtc->enabled)
                return;
 
-       if (ASIC_IS_AVIVO(rdev))
+       if (ASIC_IS_DCE4(rdev))
+               evergreen_crtc_load_lut(crtc);
+       else if (ASIC_IS_AVIVO(rdev))
                avivo_crtc_load_lut(crtc);
        else
                legacy_crtc_load_lut(crtc);
@@ -361,6 +393,8 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
 
 int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
 {
+       struct drm_device *dev = radeon_connector->base.dev;
+       struct radeon_device *rdev = dev->dev_private;
        int ret = 0;
 
        if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
@@ -373,11 +407,11 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
        if (!radeon_connector->ddc_bus)
                return -1;
        if (!radeon_connector->edid) {
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        }
-
+       /* some servers provide a hardcoded edid in rom for KVMs */
+       if (!radeon_connector->edid)
+               radeon_connector->edid = radeon_combios_get_hardcoded_edid(rdev);
        if (radeon_connector->edid) {
                drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
                ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
@@ -395,9 +429,7 @@ static int radeon_ddc_dump(struct drm_connector *connector)
 
        if (!radeon_connector->ddc_bus)
                return -1;
-       radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
        edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
-       radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        if (edid) {
                kfree(edid);
        }
@@ -414,13 +446,13 @@ static inline uint32_t radeon_div(uint64_t n, uint32_t d)
        return n;
 }
 
-void radeon_compute_pll(struct radeon_pll *pll,
-                       uint64_t freq,
-                       uint32_t *dot_clock_p,
-                       uint32_t *fb_div_p,
-                       uint32_t *frac_fb_div_p,
-                       uint32_t *ref_div_p,
-                       uint32_t *post_div_p)
+static void radeon_compute_pll_legacy(struct radeon_pll *pll,
+                                     uint64_t freq,
+                                     uint32_t *dot_clock_p,
+                                     uint32_t *fb_div_p,
+                                     uint32_t *frac_fb_div_p,
+                                     uint32_t *ref_div_p,
+                                     uint32_t *post_div_p)
 {
        uint32_t min_ref_div = pll->min_ref_div;
        uint32_t max_ref_div = pll->max_ref_div;
@@ -580,95 +612,194 @@ void radeon_compute_pll(struct radeon_pll *pll,
        *post_div_p = best_post_div;
 }
 
-void radeon_compute_pll_avivo(struct radeon_pll *pll,
-                             uint64_t freq,
-                             uint32_t *dot_clock_p,
-                             uint32_t *fb_div_p,
-                             uint32_t *frac_fb_div_p,
-                             uint32_t *ref_div_p,
-                             uint32_t *post_div_p)
+static bool
+calc_fb_div(struct radeon_pll *pll,
+           uint32_t freq,
+            uint32_t post_div,
+            uint32_t ref_div,
+            uint32_t *fb_div,
+            uint32_t *fb_div_frac)
 {
-       fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq;
-       fixed20_12 pll_out_max, pll_out_min;
-       fixed20_12 pll_in_max, pll_in_min;
-       fixed20_12 reference_freq;
-       fixed20_12 error, ffreq, a, b;
-
-       pll_out_max.full = rfixed_const(pll->pll_out_max);
-       pll_out_min.full = rfixed_const(pll->pll_out_min);
-       pll_in_max.full = rfixed_const(pll->pll_in_max);
-       pll_in_min.full = rfixed_const(pll->pll_in_min);
-       reference_freq.full = rfixed_const(pll->reference_freq);
-       do_div(freq, 10);
+       fixed20_12 feedback_divider, a, b;
+       u32 vco_freq;
+
+       vco_freq = freq * post_div;
+       /* feedback_divider = vco_freq * ref_div / pll->reference_freq; */
+       a.full = rfixed_const(pll->reference_freq);
+       feedback_divider.full = rfixed_const(vco_freq);
+       feedback_divider.full = rfixed_div(feedback_divider, a);
+       a.full = rfixed_const(ref_div);
+       feedback_divider.full = rfixed_mul(feedback_divider, a);
+
+       if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
+               /* feedback_divider = floor((feedback_divider * 10.0) + 0.5) * 0.1; */
+               a.full = rfixed_const(10);
+               feedback_divider.full = rfixed_mul(feedback_divider, a);
+               feedback_divider.full += rfixed_const_half(0);
+               feedback_divider.full = rfixed_floor(feedback_divider);
+               feedback_divider.full = rfixed_div(feedback_divider, a);
+
+               /* *fb_div = floor(feedback_divider); */
+               a.full = rfixed_floor(feedback_divider);
+               *fb_div = rfixed_trunc(a);
+               /* *fb_div_frac = fmod(feedback_divider, 1.0) * 10.0; */
+               a.full = rfixed_const(10);
+               b.full = rfixed_mul(feedback_divider, a);
+
+               feedback_divider.full = rfixed_floor(feedback_divider);
+               feedback_divider.full = rfixed_mul(feedback_divider, a);
+               feedback_divider.full = b.full - feedback_divider.full;
+               *fb_div_frac = rfixed_trunc(feedback_divider);
+       } else {
+               /* *fb_div = floor(feedback_divider + 0.5); */
+               feedback_divider.full += rfixed_const_half(0);
+               feedback_divider.full = rfixed_floor(feedback_divider);
+
+               *fb_div = rfixed_trunc(feedback_divider);
+               *fb_div_frac = 0;
+       }
+
+       if (((*fb_div) < pll->min_feedback_div) || ((*fb_div) > pll->max_feedback_div))
+               return false;
+       else
+               return true;
+}
+
+static bool
+calc_fb_ref_div(struct radeon_pll *pll,
+               uint32_t freq,
+               uint32_t post_div,
+               uint32_t *fb_div,
+                uint32_t *fb_div_frac,
+                uint32_t *ref_div)
+{
+       fixed20_12 ffreq, max_error, error, pll_out, a;
+       u32 vco;
+
        ffreq.full = rfixed_const(freq);
-       error.full = rfixed_const(100 * 100);
+       /* max_error = ffreq * 0.0025; */
+       a.full = rfixed_const(400);
+       max_error.full = rfixed_div(ffreq, a);
 
-       /* max p */
-       p.full = rfixed_div(pll_out_max, ffreq);
-       p.full = rfixed_floor(p);
+       for ((*ref_div) = pll->min_ref_div; (*ref_div) < pll->max_ref_div; ++(*ref_div)) {
+               if (calc_fb_div(pll, freq, post_div, (*ref_div), fb_div, fb_div_frac)) {
+                       vco = pll->reference_freq * (((*fb_div) * 10) + (*fb_div_frac));
+                       vco = vco / ((*ref_div) * 10);
 
-       /* min m */
-       m.full = rfixed_div(reference_freq, pll_in_max);
-       m.full = rfixed_ceil(m);
+                       if ((vco < pll->pll_out_min) || (vco > pll->pll_out_max))
+                               continue;
 
-       while (1) {
-               n.full = rfixed_div(ffreq, reference_freq);
-               n.full = rfixed_mul(n, m);
-               n.full = rfixed_mul(n, p);
+                       /* pll_out = vco / post_div; */
+                       a.full = rfixed_const(post_div);
+                       pll_out.full = rfixed_const(vco);
+                       pll_out.full = rfixed_div(pll_out, a);
 
-               f_vco.full = rfixed_div(n, m);
-               f_vco.full = rfixed_mul(f_vco, reference_freq);
+                       if (pll_out.full >= ffreq.full) {
+                               error.full = pll_out.full - ffreq.full;
+                               if (error.full <= max_error.full)
+                                       return true;
+                       }
+               }
+       }
+       return false;
+}
 
-               f_pclk.full = rfixed_div(f_vco, p);
+static void radeon_compute_pll_new(struct radeon_pll *pll,
+                                  uint64_t freq,
+                                  uint32_t *dot_clock_p,
+                                  uint32_t *fb_div_p,
+                                  uint32_t *frac_fb_div_p,
+                                  uint32_t *ref_div_p,
+                                  uint32_t *post_div_p)
+{
+       u32 fb_div = 0, fb_div_frac = 0, post_div = 0, ref_div = 0;
+       u32 best_freq = 0, vco_frequency;
 
-               if (f_pclk.full > ffreq.full)
-                       error.full = f_pclk.full - ffreq.full;
-               else
-                       error.full = ffreq.full - f_pclk.full;
-               error.full = rfixed_div(error, f_pclk);
-               a.full = rfixed_const(100 * 100);
-               error.full = rfixed_mul(error, a);
-
-               a.full = rfixed_mul(m, p);
-               a.full = rfixed_div(n, a);
-               best_freq.full = rfixed_mul(reference_freq, a);
-
-               if (rfixed_trunc(error) < 25)
-                       break;
-
-               a.full = rfixed_const(1);
-               m.full = m.full + a.full;
-               a.full = rfixed_div(reference_freq, m);
-               if (a.full >= pll_in_min.full)
-                       continue;
+       /* freq = freq / 10; */
+       do_div(freq, 10);
 
-               m.full = rfixed_div(reference_freq, pll_in_max);
-               m.full = rfixed_ceil(m);
-               a.full= rfixed_const(1);
-               p.full = p.full - a.full;
-               a.full = rfixed_mul(p, ffreq);
-               if (a.full >= pll_out_min.full)
-                       continue;
-               else {
-                       DRM_ERROR("Unable to find pll dividers\n");
-                       break;
+       if (pll->flags & RADEON_PLL_USE_POST_DIV) {
+               post_div = pll->post_div;
+               if ((post_div < pll->min_post_div) || (post_div > pll->max_post_div))
+                       goto done;
+
+               vco_frequency = freq * post_div;
+               if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max))
+                       goto done;
+
+               if (pll->flags & RADEON_PLL_USE_REF_DIV) {
+                       ref_div = pll->reference_div;
+                       if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div))
+                               goto done;
+                       if (!calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac))
+                               goto done;
+               }
+       } else {
+               for (post_div = pll->max_post_div; post_div >= pll->min_post_div; --post_div) {
+                       if (pll->flags & RADEON_PLL_LEGACY) {
+                               if ((post_div == 5) ||
+                                   (post_div == 7) ||
+                                   (post_div == 9) ||
+                                   (post_div == 10) ||
+                                   (post_div == 11))
+                                       continue;
+                       }
+
+                       if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
+                               continue;
+
+                       vco_frequency = freq * post_div;
+                       if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max))
+                               continue;
+                       if (pll->flags & RADEON_PLL_USE_REF_DIV) {
+                               ref_div = pll->reference_div;
+                               if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div))
+                                       goto done;
+                               if (calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac))
+                                       break;
+                       } else {
+                               if (calc_fb_ref_div(pll, freq, post_div, &fb_div, &fb_div_frac, &ref_div))
+                                       break;
+                       }
                }
        }
 
-       a.full = rfixed_const(10);
-       b.full = rfixed_mul(n, a);
+       best_freq = pll->reference_freq * 10 * fb_div;
+       best_freq += pll->reference_freq * fb_div_frac;
+       best_freq = best_freq / (ref_div * post_div);
 
-       frac_n.full = rfixed_floor(n);
-       frac_n.full = rfixed_mul(frac_n, a);
-       frac_n.full = b.full - frac_n.full;
+done:
+       if (best_freq == 0)
+               DRM_ERROR("Couldn't find valid PLL dividers\n");
 
-       *dot_clock_p = rfixed_trunc(best_freq);
-       *fb_div_p = rfixed_trunc(n);
-       *frac_fb_div_p = rfixed_trunc(frac_n);
-       *ref_div_p = rfixed_trunc(m);
-       *post_div_p = rfixed_trunc(p);
+       *dot_clock_p = best_freq / 10;
+       *fb_div_p = fb_div;
+       *frac_fb_div_p = fb_div_frac;
+       *ref_div_p = ref_div;
+       *post_div_p = post_div;
 
-       DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
+       DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
+}
+
+void radeon_compute_pll(struct radeon_pll *pll,
+                       uint64_t freq,
+                       uint32_t *dot_clock_p,
+                       uint32_t *fb_div_p,
+                       uint32_t *frac_fb_div_p,
+                       uint32_t *ref_div_p,
+                       uint32_t *post_div_p)
+{
+       switch (pll->algo) {
+       case PLL_ALGO_NEW:
+               radeon_compute_pll_new(pll, freq, dot_clock_p, fb_div_p,
+                                      frac_fb_div_p, ref_div_p, post_div_p);
+               break;
+       case PLL_ALGO_LEGACY:
+       default:
+               radeon_compute_pll_legacy(pll, freq, dot_clock_p, fb_div_p,
+                                         frac_fb_div_p, ref_div_p, post_div_p);
+               break;
+       }
 }
 
 static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
@@ -679,11 +810,8 @@ static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
        if (fb->fbdev)
                radeonfb_remove(dev, fb);
 
-       if (radeon_fb->obj) {
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(radeon_fb->obj);
-               mutex_unlock(&dev->struct_mutex);
-       }
+       if (radeon_fb->obj)
+               drm_gem_object_unreference_unlocked(radeon_fb->obj);
        drm_framebuffer_cleanup(fb);
        kfree(radeon_fb);
 }
@@ -819,7 +947,7 @@ static int radeon_modeset_create_props(struct radeon_device *rdev)
 
 int radeon_modeset_init(struct radeon_device *rdev)
 {
-       int num_crtc = 2, i;
+       int i;
        int ret;
 
        drm_mode_config_init(rdev->ddev);
@@ -842,11 +970,23 @@ int radeon_modeset_init(struct radeon_device *rdev)
                return ret;
        }
 
+       /* check combios for a valid hardcoded EDID - Sun servers */
+       if (!rdev->is_atom_bios) {
+               /* check for hardcoded EDID in BIOS */
+               radeon_combios_check_hardcoded_edid(rdev);
+       }
+
        if (rdev->flags & RADEON_SINGLE_CRTC)
-               num_crtc = 1;
+               rdev->num_crtc = 1;
+       else {
+               if (ASIC_IS_DCE4(rdev))
+                       rdev->num_crtc = 6;
+               else
+                       rdev->num_crtc = 2;
+       }
 
        /* allocate crtcs */
-       for (i = 0; i < num_crtc; i++) {
+       for (i = 0; i < rdev->num_crtc; i++) {
                radeon_crtc_init(rdev->ddev, i);
        }
 
@@ -863,6 +1003,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
 
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
+       kfree(rdev->mode_info.bios_hardcoded_edid);
+
        if (rdev->mode_info.mode_config_initialized) {
                radeon_hpd_fini(rdev);
                drm_mode_config_cleanup(rdev->ddev);
index 8ba3de7994d4f2719313f0baf7f62d0daebdfe23..6eec0ece6a6ccd6d14e9ae05a2fee63d0879ce41 100644 (file)
 
 /*
  * KMS wrapper.
+ * - 2.0.0 - initial interface
+ * - 2.1.0 - add square tiling interface
  */
 #define KMS_DRIVER_MAJOR       2
-#define KMS_DRIVER_MINOR       0
+#define KMS_DRIVER_MINOR       1
 #define KMS_DRIVER_PATCHLEVEL  0
 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
 int radeon_driver_unload_kms(struct drm_device *dev);
@@ -86,7 +88,8 @@ int radeon_benchmarking = 0;
 int radeon_testing = 0;
 int radeon_connector_table = 0;
 int radeon_tv = 1;
-int radeon_new_pll = 1;
+int radeon_new_pll = -1;
+int radeon_dynpm = -1;
 int radeon_audio = 1;
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
@@ -122,9 +125,12 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
 MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
 module_param_named(tv, radeon_tv, int, 0444);
 
-MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips");
+MODULE_PARM_DESC(new_pll, "Select new PLL code");
 module_param_named(new_pll, radeon_new_pll, int, 0444);
 
+MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)");
+module_param_named(dynpm, radeon_dynpm, int, 0444);
+
 MODULE_PARM_DESC(audio, "Audio enable (0 = disable)");
 module_param_named(audio, radeon_audio, int, 0444);
 
@@ -339,6 +345,7 @@ static int __init radeon_init(void)
                driver = &kms_driver;
                driver->driver_features |= DRIVER_MODESET;
                driver->num_ioctls = radeon_max_kms_ioctl;
+               radeon_register_atpx_handler();
        }
        /* if the vga console setting is enabled still
         * let modprobe override it */
@@ -348,6 +355,7 @@ static int __init radeon_init(void)
 static void __exit radeon_exit(void)
 {
        drm_exit(driver);
+       radeon_unregister_atpx_handler();
 }
 
 module_init(radeon_init);
index c57ad606504dfcb3cee7b66103aac1ab834daec9..ec55f2b23c2298f39d7b108d183bf52a59ddad34 100644 (file)
@@ -268,6 +268,8 @@ typedef struct drm_radeon_private {
 
        u32 scratch_ages[5];
 
+       int have_z_offset;
+
        /* starting from here on, data is preserved accross an open */
        uint32_t flags;         /* see radeon_chip_flags */
        resource_size_t fb_aper_offset;
@@ -295,6 +297,9 @@ typedef struct drm_radeon_private {
        int r700_sc_prim_fifo_size;
        int r700_sc_hiz_tile_fifo_size;
        int r700_sc_earlyz_tile_fifo_fize;
+       int r600_group_size;
+       int r600_npipes;
+       int r600_nbanks;
 
        struct mutex cs_mutex;
        u32 cs_id_scnt;
@@ -310,9 +315,11 @@ typedef struct drm_radeon_buf_priv {
        u32 age;
 } drm_radeon_buf_priv_t;
 
+struct drm_buffer;
+
 typedef struct drm_radeon_kcmd_buffer {
        int bufsz;
-       char *buf;
+       struct drm_buffer *buffer;
        int nbox;
        struct drm_clip_rect __user *boxes;
 } drm_radeon_kcmd_buffer_t;
@@ -455,6 +462,15 @@ extern void r600_blit_swap(struct drm_device *dev,
                           int sx, int sy, int dx, int dy,
                           int w, int h, int src_pitch, int dst_pitch, int cpp);
 
+/* atpx handler */
+#if defined(CONFIG_VGA_SWITCHEROO)
+void radeon_register_atpx_handler(void);
+void radeon_unregister_atpx_handler(void);
+#else
+static inline void radeon_register_atpx_handler(void) {}
+static inline void radeon_unregister_atpx_handler(void) {}
+#endif
+
 /* Flags for stats.boxes
  */
 #define RADEON_BOX_DMA_IDLE      0x1
@@ -2122,4 +2138,32 @@ extern void radeon_commit_ring(drm_radeon_private_t *dev_priv);
        write &= mask;                                          \
 } while (0)
 
+/**
+ * Copy given number of dwords from drm buffer to the ring buffer.
+ */
+#define OUT_RING_DRM_BUFFER(buf, sz) do {                              \
+       int _size = (sz) * 4;                                           \
+       struct drm_buffer *_buf = (buf);                                \
+       int _part_size;                                                 \
+       while (_size > 0) {                                             \
+               _part_size = _size;                                     \
+                                                                       \
+               if (write + _part_size/4 > mask)                        \
+                       _part_size = ((mask + 1) - write)*4;            \
+                                                                       \
+               if (drm_buffer_index(_buf) + _part_size > PAGE_SIZE)    \
+                       _part_size = PAGE_SIZE - drm_buffer_index(_buf);\
+                                                                       \
+                                                                       \
+                                                                       \
+               memcpy(ring + write, &_buf->data[drm_buffer_page(_buf)] \
+                       [drm_buffer_index(_buf)], _part_size);          \
+                                                                       \
+               _size -= _part_size;                                    \
+               write = (write + _part_size/4) & mask;                  \
+               drm_buffer_advance(_buf, _part_size);                   \
+       }                                                               \
+} while (0)
+
+
 #endif                         /* __RADEON_DRV_H__ */
index 3c91724457ca3f69bced25185cf8cc14eace9ea1..bc926ea0a530e014c2b075fed8ac35e0a94d04ae 100644 (file)
@@ -53,7 +53,7 @@ static uint32_t radeon_encoder_clones(struct drm_encoder *encoder)
        /* DVO requires 2x ppll clocks depending on tmds chip */
        if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT)
                return index_mask;
-       
+
        count = -1;
        list_for_each_entry(clone_encoder, &dev->mode_config.encoder_list, head) {
                struct radeon_encoder *radeon_clone = to_radeon_encoder(clone_encoder);
@@ -228,6 +228,32 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
        return NULL;
 }
 
+static struct radeon_connector_atom_dig *
+radeon_get_atom_connector_priv_from_encoder(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector;
+       struct radeon_connector_atom_dig *dig_connector;
+
+       if (!rdev->is_atom_bios)
+               return NULL;
+
+       connector = radeon_get_connector_for_encoder(encoder);
+       if (!connector)
+               return NULL;
+
+       radeon_connector = to_radeon_connector(connector);
+
+       if (!radeon_connector->con_priv)
+               return NULL;
+
+       dig_connector = radeon_connector->con_priv;
+
+       return dig_connector;
+}
+
 static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
                                   struct drm_display_mode *mode,
                                   struct drm_display_mode *adjusted_mode)
@@ -236,6 +262,9 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
 
+       /* adjust pm to upcoming mode change */
+       radeon_pm_compute_clocks(rdev);
+
        /* set the active encoder to connector routing */
        radeon_encoder_set_active_device(encoder);
        drm_mode_set_crtcinfo(adjusted_mode, 0);
@@ -458,34 +487,20 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       struct radeon_connector_atom_dig *dig_connector =
+               radeon_get_atom_connector_priv_from_encoder(encoder);
        union lvds_encoder_control args;
        int index = 0;
        int hdmi_detected = 0;
        uint8_t frev, crev;
-       struct radeon_encoder_atom_dig *dig;
-       struct drm_connector *connector;
-       struct radeon_connector *radeon_connector;
-       struct radeon_connector_atom_dig *dig_connector;
 
-       connector = radeon_get_connector_for_encoder(encoder);
-       if (!connector)
+       if (!dig || !dig_connector)
                return;
 
-       radeon_connector = to_radeon_connector(connector);
-
-       if (!radeon_encoder->enc_priv)
-               return;
-
-       dig = radeon_encoder->enc_priv;
-
-       if (!radeon_connector->con_priv)
-               return;
-
-       if (drm_detect_hdmi_monitor(radeon_connector->edid))
+       if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
                hdmi_detected = 1;
 
-       dig_connector = radeon_connector->con_priv;
-
        memset(&args, 0, sizeof(args));
 
        switch (radeon_encoder->encoder_id) {
@@ -586,7 +601,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
 {
        struct drm_connector *connector;
        struct radeon_connector *radeon_connector;
-       struct radeon_connector_atom_dig *radeon_dig_connector;
+       struct radeon_connector_atom_dig *dig_connector;
 
        connector = radeon_get_connector_for_encoder(encoder);
        if (!connector)
@@ -617,9 +632,9 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
                break;
        case DRM_MODE_CONNECTOR_DisplayPort:
        case DRM_MODE_CONNECTOR_eDP:
-               radeon_dig_connector = radeon_connector->con_priv;
-               if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
-                   (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
+               dig_connector = radeon_connector->con_priv;
+               if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
+                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
                        return ATOM_ENCODER_MODE_DP;
                else if (drm_detect_hdmi_monitor(radeon_connector->edid))
                        return ATOM_ENCODER_MODE_HDMI;
@@ -656,6 +671,18 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
  * - 2 DIG encoder blocks.
  * DIG1/2 can drive UNIPHY0/1/2 link A or link B
  *
+ * DCE 4.0
+ * - 3 DIG transmitter blocks UNPHY0/1/2 (links A and B).
+ * Supports up to 6 digital outputs
+ * - 6 DIG encoder blocks.
+ * - DIG to PHY mapping is hardcoded
+ * DIG1 drives UNIPHY0 link A, A+B
+ * DIG2 drives UNIPHY0 link B
+ * DIG3 drives UNIPHY1 link A, A+B
+ * DIG4 drives UNIPHY1 link B
+ * DIG5 drives UNIPHY2 link A, A+B
+ * DIG6 drives UNIPHY2 link B
+ *
  * Routing
  * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
  * Examples:
@@ -664,88 +691,78 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
  * crtc0 -> dig1 -> UNIPHY2 link  A   -> LVDS
  * crtc1 -> dig2 -> UNIPHY1 link  B+A -> TMDS/HDMI
  */
-static void
+
+union dig_encoder_control {
+       DIG_ENCODER_CONTROL_PS_ALLOCATION v1;
+       DIG_ENCODER_CONTROL_PARAMETERS_V2 v2;
+       DIG_ENCODER_CONTROL_PARAMETERS_V3 v3;
+};
+
+void
 atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
 {
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
-       DIG_ENCODER_CONTROL_PS_ALLOCATION args;
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       struct radeon_connector_atom_dig *dig_connector =
+               radeon_get_atom_connector_priv_from_encoder(encoder);
+       union dig_encoder_control args;
        int index = 0, num = 0;
        uint8_t frev, crev;
-       struct radeon_encoder_atom_dig *dig;
-       struct drm_connector *connector;
-       struct radeon_connector *radeon_connector;
-       struct radeon_connector_atom_dig *dig_connector;
 
-       connector = radeon_get_connector_for_encoder(encoder);
-       if (!connector)
+       if (!dig || !dig_connector)
                return;
 
-       radeon_connector = to_radeon_connector(connector);
-
-       if (!radeon_connector->con_priv)
-               return;
-
-       dig_connector = radeon_connector->con_priv;
-
-       if (!radeon_encoder->enc_priv)
-               return;
-
-       dig = radeon_encoder->enc_priv;
-
        memset(&args, 0, sizeof(args));
 
-       if (dig->dig_encoder)
-               index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
-       else
-               index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+       if (ASIC_IS_DCE4(rdev))
+               index = GetIndexIntoMasterTable(COMMAND, DIGxEncoderControl);
+       else {
+               if (dig->dig_encoder)
+                       index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
+               else
+                       index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+       }
        num = dig->dig_encoder + 1;
 
        atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev);
 
-       args.ucAction = action;
-       args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+       args.v1.ucAction = action;
+       args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+       args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder);
 
-       if (ASIC_IS_DCE32(rdev)) {
+       if (args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
+               if (dig_connector->dp_clock == 270000)
+                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
+               args.v1.ucLaneNum = dig_connector->dp_lane_count;
+       } else if (radeon_encoder->pixel_clock > 165000)
+               args.v1.ucLaneNum = 8;
+       else
+               args.v1.ucLaneNum = 4;
+
+       if (ASIC_IS_DCE4(rdev)) {
+               args.v3.acConfig.ucDigSel = dig->dig_encoder;
+               args.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+       } else {
                switch (radeon_encoder->encoder_id) {
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
-                       args.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1;
+                       args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER1;
                        break;
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
-                       args.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2;
+               case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+                       args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER2;
                        break;
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
-                       args.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3;
-                       break;
-               }
-       } else {
-               switch (radeon_encoder->encoder_id) {
-               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
-                       args.ucConfig = ATOM_ENCODER_CONFIG_TRANSMITTER1;
-                       break;
-               case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
-                       args.ucConfig = ATOM_ENCODER_CONFIG_TRANSMITTER2;
+                       args.v1.ucConfig = ATOM_ENCODER_CONFIG_V2_TRANSMITTER3;
                        break;
                }
+               if (dig_connector->linkb)
+                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
+               else
+                       args.v1.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
        }
 
-       args.ucEncoderMode = atombios_get_encoder_mode(encoder);
-
-       if (args.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
-               if (dig_connector->dp_clock == 270000)
-                       args.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
-               args.ucLaneNum = dig_connector->dp_lane_count;
-       } else if (radeon_encoder->pixel_clock > 165000)
-               args.ucLaneNum = 8;
-       else
-               args.ucLaneNum = 4;
-
-       if (dig_connector->linkb)
-               args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
-       else
-               args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
-
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
 }
@@ -753,6 +770,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
 union dig_transmitter_control {
        DIG_TRANSMITTER_CONTROL_PS_ALLOCATION v1;
        DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
+       DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3;
 };
 
 void
@@ -761,37 +779,29 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+       struct radeon_connector_atom_dig *dig_connector =
+               radeon_get_atom_connector_priv_from_encoder(encoder);
+       struct drm_connector *connector;
+       struct radeon_connector *radeon_connector;
        union dig_transmitter_control args;
        int index = 0, num = 0;
        uint8_t frev, crev;
-       struct radeon_encoder_atom_dig *dig;
-       struct drm_connector *connector;
-       struct radeon_connector *radeon_connector;
-       struct radeon_connector_atom_dig *dig_connector;
        bool is_dp = false;
+       int pll_id = 0;
 
-       connector = radeon_get_connector_for_encoder(encoder);
-       if (!connector)
+       if (!dig || !dig_connector)
                return;
 
+       connector = radeon_get_connector_for_encoder(encoder);
        radeon_connector = to_radeon_connector(connector);
 
-       if (!radeon_encoder->enc_priv)
-               return;
-
-       dig = radeon_encoder->enc_priv;
-
-       if (!radeon_connector->con_priv)
-               return;
-
-       dig_connector = radeon_connector->con_priv;
-
        if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP)
                is_dp = true;
 
        memset(&args, 0, sizeof(args));
 
-       if (ASIC_IS_DCE32(rdev))
+       if (ASIC_IS_DCE32(rdev) || ASIC_IS_DCE4(rdev))
                index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl);
        else {
                switch (radeon_encoder->encoder_id) {
@@ -821,7 +831,54 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
                else
                        args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
        }
-       if (ASIC_IS_DCE32(rdev)) {
+       if (ASIC_IS_DCE4(rdev)) {
+               if (is_dp)
+                       args.v3.ucLaneNum = dig_connector->dp_lane_count;
+               else if (radeon_encoder->pixel_clock > 165000)
+                       args.v3.ucLaneNum = 8;
+               else
+                       args.v3.ucLaneNum = 4;
+
+               if (dig_connector->linkb) {
+                       args.v3.acConfig.ucLinkSel = 1;
+                       args.v3.acConfig.ucEncoderSel = 1;
+               }
+
+               /* Select the PLL for the PHY
+                * DP PHY should be clocked from external src if there is
+                * one.
+                */
+               if (encoder->crtc) {
+                       struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+                       pll_id = radeon_crtc->pll_id;
+               }
+               if (is_dp && rdev->clock.dp_extclk)
+                       args.v3.acConfig.ucRefClkSource = 2; /* external src */
+               else
+                       args.v3.acConfig.ucRefClkSource = pll_id;
+
+               switch (radeon_encoder->encoder_id) {
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+                       args.v3.acConfig.ucTransmitterSel = 0;
+                       num = 0;
+                       break;
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+                       args.v3.acConfig.ucTransmitterSel = 1;
+                       num = 1;
+                       break;
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+                       args.v3.acConfig.ucTransmitterSel = 2;
+                       num = 2;
+                       break;
+               }
+
+               if (is_dp)
+                       args.v3.acConfig.fCoherentMode = 1; /* DP requires coherent */
+               else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+                       if (dig->coherent_mode)
+                               args.v3.acConfig.fCoherentMode = 1;
+               }
+       } else if (ASIC_IS_DCE32(rdev)) {
                if (dig->dig_encoder == 1)
                        args.v2.acConfig.ucEncoderSel = 1;
                if (dig_connector->linkb)
@@ -849,7 +906,6 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
                                args.v2.acConfig.fCoherentMode = 1;
                }
        } else {
-
                args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;
 
                if (dig->dig_encoder)
@@ -1024,9 +1080,12 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
                atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
        }
        radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+       /* adjust pm to dpms change */
+       radeon_pm_compute_clocks(rdev);
 }
 
-union crtc_sourc_param {
+union crtc_source_param {
        SELECT_CRTC_SOURCE_PS_ALLOCATION v1;
        SELECT_CRTC_SOURCE_PARAMETERS_V2 v2;
 };
@@ -1038,7 +1097,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
-       union crtc_sourc_param args;
+       union crtc_source_param args;
        int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
        uint8_t frev, crev;
        struct radeon_encoder_atom_dig *dig;
@@ -1107,10 +1166,26 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
                        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                                dig = radeon_encoder->enc_priv;
-                               if (dig->dig_encoder)
-                                       args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
-                               else
+                               switch (dig->dig_encoder) {
+                               case 0:
                                        args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
+                                       break;
+                               case 1:
+                                       args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
+                                       break;
+                               case 2:
+                                       args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID;
+                                       break;
+                               case 3:
+                                       args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID;
+                                       break;
+                               case 4:
+                                       args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID;
+                                       break;
+                               case 5:
+                                       args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
+                                       break;
+                               }
                                break;
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
                                args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
@@ -1167,6 +1242,7 @@ atombios_apply_encoder_quirks(struct drm_encoder *encoder,
        }
 
        /* set scaler clears this on some chips */
+       /* XXX check DCE4 */
        if (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))) {
                if (ASIC_IS_AVIVO(rdev) && (mode->flags & DRM_MODE_FLAG_INTERLACE))
                        WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset,
@@ -1183,6 +1259,33 @@ static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
        struct drm_encoder *test_encoder;
        struct radeon_encoder_atom_dig *dig;
        uint32_t dig_enc_in_use = 0;
+
+       if (ASIC_IS_DCE4(rdev)) {
+               struct radeon_connector_atom_dig *dig_connector =
+                       radeon_get_atom_connector_priv_from_encoder(encoder);
+
+               switch (radeon_encoder->encoder_id) {
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+                       if (dig_connector->linkb)
+                               return 1;
+                       else
+                               return 0;
+                       break;
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+                       if (dig_connector->linkb)
+                               return 3;
+                       else
+                               return 2;
+                       break;
+               case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+                       if (dig_connector->linkb)
+                               return 5;
+                       else
+                               return 4;
+                       break;
+               }
+       }
+
        /* on DCE32 and encoder can driver any block so just crtc id */
        if (ASIC_IS_DCE32(rdev)) {
                return radeon_crtc->crtc_id;
@@ -1254,15 +1357,26 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
-               /* disable the encoder and transmitter */
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
-               atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
-
-               /* setup and enable the encoder and transmitter */
-               atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+               if (ASIC_IS_DCE4(rdev)) {
+                       /* disable the transmitter */
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+                       /* setup and enable the encoder */
+                       atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP);
+
+                       /* init and enable the transmitter */
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+               } else {
+                       /* disable the encoder and transmitter */
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+                       atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
+
+                       /* setup and enable the encoder and transmitter */
+                       atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+               }
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DDI:
                atombios_ddia_setup(encoder, ATOM_ENABLE);
@@ -1282,7 +1396,9 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
        }
        atombios_apply_encoder_quirks(encoder, adjusted_mode);
 
-       r600_hdmi_setmode(encoder, adjusted_mode);
+       /* XXX */
+       if (!ASIC_IS_DCE4(rdev))
+               r600_hdmi_setmode(encoder, adjusted_mode);
 }
 
 static bool
@@ -1480,10 +1596,18 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
                return;
 
        encoder = &radeon_encoder->base;
-       if (rdev->flags & RADEON_SINGLE_CRTC)
+       switch (rdev->num_crtc) {
+       case 1:
                encoder->possible_crtcs = 0x1;
-       else
+               break;
+       case 2:
+       default:
                encoder->possible_crtcs = 0x3;
+               break;
+       case 6:
+               encoder->possible_crtcs = 0x3f;
+               break;
+       }
 
        radeon_encoder->enc_priv = NULL;
 
index 797972e344a64ce70a6e79aca50657146ca80d56..93c7d5d419141456cdbe8b0c455b78709b993439 100644 (file)
@@ -75,6 +75,11 @@ enum radeon_family {
        CHIP_RV730,
        CHIP_RV710,
        CHIP_RV740,
+       CHIP_CEDAR,
+       CHIP_REDWOOD,
+       CHIP_JUNIPER,
+       CHIP_CYPRESS,
+       CHIP_HEMLOCK,
        CHIP_LAST,
 };
 
index d71e346e9ab5a9faa96d62a96ad35cb84cfce20d..8fccbf29235e76efe2e7aecf12d53253e3f896c7 100644 (file)
@@ -39,6 +39,8 @@
 
 #include "drm_fb_helper.h"
 
+#include <linux/vga_switcheroo.h>
+
 struct radeon_fb_device {
        struct drm_fb_helper helper;
        struct radeon_framebuffer       *rfb;
@@ -148,7 +150,6 @@ int radeonfb_create(struct drm_device *dev,
        unsigned long tmp;
        bool fb_tiled = false; /* useful for testing */
        u32 tiling_flags = 0;
-       int crtc_count;
 
        mode_cmd.width = surface_width;
        mode_cmd.height = surface_height;
@@ -239,11 +240,7 @@ int radeonfb_create(struct drm_device *dev,
        rfbdev = info->par;
        rfbdev->helper.funcs = &radeon_fb_helper_funcs;
        rfbdev->helper.dev = dev;
-       if (rdev->flags & RADEON_SINGLE_CRTC)
-               crtc_count = 1;
-       else
-               crtc_count = 2;
-       ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, crtc_count,
+       ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, rdev->num_crtc,
                                            RADEONFB_CONN_LIMIT);
        if (ret)
                goto out_unref;
@@ -257,7 +254,7 @@ int radeonfb_create(struct drm_device *dev,
        info->flags = FBINFO_DEFAULT;
        info->fbops = &radeonfb_ops;
 
-       tmp = fb_gpuaddr - rdev->mc.vram_location;
+       tmp = fb_gpuaddr - rdev->mc.vram_start;
        info->fix.smem_start = rdev->mc.aper_base + tmp;
        info->fix.smem_len = size;
        info->screen_base = fbptr;
@@ -291,6 +288,7 @@ int radeonfb_create(struct drm_device *dev,
        rfbdev->rdev = rdev;
 
        mutex_unlock(&rdev->ddev->struct_mutex);
+       vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
        return 0;
 
 out_unref:
index e73d56e83fa68760e6a14dd70d74fcd1473ed0ea..1770d3c07fd03dc8563adce502763b62a3320c0d 100644 (file)
@@ -139,6 +139,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
        unsigned t;
        unsigned p;
        int i, j;
+       u64 page_base;
 
        if (!rdev->gart.ready) {
                WARN(1, "trying to unbind memory to unitialized GART !\n");
@@ -151,9 +152,11 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
                        pci_unmap_page(rdev->pdev, rdev->gart.pages_addr[p],
                                       PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
                        rdev->gart.pages[p] = NULL;
-                       rdev->gart.pages_addr[p] = 0;
+                       rdev->gart.pages_addr[p] = rdev->dummy_page.addr;
+                       page_base = rdev->gart.pages_addr[p];
                        for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
-                               radeon_gart_set_page(rdev, t, 0);
+                               radeon_gart_set_page(rdev, t, page_base);
+                               page_base += RADEON_GPU_PAGE_SIZE;
                        }
                }
        }
@@ -199,8 +202,26 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
        return 0;
 }
 
+void radeon_gart_restore(struct radeon_device *rdev)
+{
+       int i, j, t;
+       u64 page_base;
+
+       for (i = 0, t = 0; i < rdev->gart.num_cpu_pages; i++) {
+               page_base = rdev->gart.pages_addr[i];
+               for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
+                       radeon_gart_set_page(rdev, t, page_base);
+                       page_base += RADEON_GPU_PAGE_SIZE;
+               }
+       }
+       mb();
+       radeon_gart_tlb_flush(rdev);
+}
+
 int radeon_gart_init(struct radeon_device *rdev)
 {
+       int r, i;
+
        if (rdev->gart.pages) {
                return 0;
        }
@@ -209,6 +230,9 @@ int radeon_gart_init(struct radeon_device *rdev)
                DRM_ERROR("Page size is smaller than GPU page size!\n");
                return -EINVAL;
        }
+       r = radeon_dummy_page_init(rdev);
+       if (r)
+               return r;
        /* Compute table size */
        rdev->gart.num_cpu_pages = rdev->mc.gtt_size / PAGE_SIZE;
        rdev->gart.num_gpu_pages = rdev->mc.gtt_size / RADEON_GPU_PAGE_SIZE;
@@ -227,6 +251,10 @@ int radeon_gart_init(struct radeon_device *rdev)
                radeon_gart_fini(rdev);
                return -ENOMEM;
        }
+       /* set GART entry to point to the dummy page by default */
+       for (i = 0; i < rdev->gart.num_cpu_pages; i++) {
+               rdev->gart.pages_addr[i] = rdev->dummy_page.addr;
+       }
        return 0;
 }
 
index db8e9a355a01624c32c16bb3ac002b90c97277b3..ef92d147d8f07ac516f7708379f662d888f3892a 100644 (file)
@@ -69,9 +69,7 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
                if (r != -ERESTARTSYS)
                        DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n",
                                  size, initial_domain, alignment, r);
-               mutex_lock(&rdev->ddev->struct_mutex);
-               drm_gem_object_unreference(gobj);
-               mutex_unlock(&rdev->ddev->struct_mutex);
+               drm_gem_object_unreference_unlocked(gobj);
                return r;
        }
        gobj->driver_private = robj;
@@ -202,14 +200,10 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
        }
        r = drm_gem_handle_create(filp, gobj, &handle);
        if (r) {
-               mutex_lock(&dev->struct_mutex);
-               drm_gem_object_unreference(gobj);
-               mutex_unlock(&dev->struct_mutex);
+               drm_gem_object_unreference_unlocked(gobj);
                return r;
        }
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_handle_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_handle_unreference_unlocked(gobj);
        args->handle = handle;
        return 0;
 }
@@ -236,9 +230,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 
        r = radeon_gem_set_domain(gobj, args->read_domains, args->write_domain);
 
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gobj);
        return r;
 }
 
@@ -255,9 +247,7 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
        }
        robj = gobj->driver_private;
        args->addr_ptr = radeon_bo_mmap_offset(robj);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gobj);
        return 0;
 }
 
@@ -288,9 +278,7 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
        default:
                break;
        }
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gobj);
        return r;
 }
 
@@ -311,9 +299,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
        /* callback hw specific functions if any */
        if (robj->rdev->asic->ioctl_wait_idle)
                robj->rdev->asic->ioctl_wait_idle(robj->rdev, robj);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gobj);
        return r;
 }
 
@@ -331,9 +317,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        robj = gobj->driver_private;
        r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch);
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gobj);
        return r;
 }
 
@@ -356,8 +340,6 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
        radeon_bo_get_tiling_flags(rbo, &args->tiling_flags, &args->pitch);
        radeon_bo_unreserve(rbo);
 out:
-       mutex_lock(&dev->struct_mutex);
-       drm_gem_object_unreference(gobj);
-       mutex_unlock(&dev->struct_mutex);
+       drm_gem_object_unreference_unlocked(gobj);
        return r;
 }
index da3da1e89d002eff60cbd5d7df5ea84e94648a94..4ae50c19589fe8b4e4f89304862515416cbe72c5 100644 (file)
@@ -26,6 +26,7 @@
 #include "drmP.h"
 #include "radeon_drm.h"
 #include "radeon.h"
+#include "atom.h"
 
 /**
  * radeon_ddc_probe
@@ -59,7 +60,7 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
 }
 
 
-void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
+static void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
 {
        struct radeon_device *rdev = i2c->dev->dev_private;
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
@@ -71,13 +72,25 @@ void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
         */
        if (rec->hw_capable) {
                if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
-                       if (rec->a_clk_reg == RADEON_GPIO_MONID) {
+                       u32 reg;
+
+                       if (rdev->family >= CHIP_RV350)
+                               reg = RADEON_GPIO_MONID;
+                       else if ((rdev->family == CHIP_R300) ||
+                                (rdev->family == CHIP_R350))
+                               reg = RADEON_GPIO_DVI_DDC;
+                       else
+                               reg = RADEON_GPIO_CRT2_DDC;
+
+                       mutex_lock(&rdev->dc_hw_i2c_mutex);
+                       if (rec->a_clk_reg == reg) {
                                WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
                                                               R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
                        } else {
                                WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
                                                               R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
                        }
+                       mutex_unlock(&rdev->dc_hw_i2c_mutex);
                }
        }
 
@@ -168,6 +181,692 @@ static void set_data(void *i2c_priv, int data)
        WREG32(rec->en_data_reg, val);
 }
 
+static u32 radeon_get_i2c_prescale(struct radeon_device *rdev)
+{
+       struct radeon_pll *spll = &rdev->clock.spll;
+       u32 sclk = radeon_get_engine_clock(rdev);
+       u32 prescale = 0;
+       u32 n, m;
+       u8 loop;
+       int i2c_clock;
+
+       switch (rdev->family) {
+       case CHIP_R100:
+       case CHIP_RV100:
+       case CHIP_RS100:
+       case CHIP_RV200:
+       case CHIP_RS200:
+       case CHIP_R200:
+       case CHIP_RV250:
+       case CHIP_RS300:
+       case CHIP_RV280:
+       case CHIP_R300:
+       case CHIP_R350:
+       case CHIP_RV350:
+               n = (spll->reference_freq) / (4 * 6);
+               for (loop = 1; loop < 255; loop++) {
+                       if ((loop * (loop - 1)) > n)
+                               break;
+               }
+               m = loop - 1;
+               prescale = m | (loop << 8);
+               break;
+       case CHIP_RV380:
+       case CHIP_RS400:
+       case CHIP_RS480:
+       case CHIP_R420:
+       case CHIP_R423:
+       case CHIP_RV410:
+               sclk = radeon_get_engine_clock(rdev);
+               prescale = (((sclk * 10)/(4 * 128 * 100) + 1) << 8) + 128;
+               break;
+       case CHIP_RS600:
+       case CHIP_RS690:
+       case CHIP_RS740:
+               /* todo */
+               break;
+       case CHIP_RV515:
+       case CHIP_R520:
+       case CHIP_RV530:
+       case CHIP_RV560:
+       case CHIP_RV570:
+       case CHIP_R580:
+               i2c_clock = 50;
+               sclk = radeon_get_engine_clock(rdev);
+               if (rdev->family == CHIP_R520)
+                       prescale = (127 << 8) + ((sclk * 10) / (4 * 127 * i2c_clock));
+               else
+                       prescale = (((sclk * 10)/(4 * 128 * 100) + 1) << 8) + 128;
+               break;
+       case CHIP_R600:
+       case CHIP_RV610:
+       case CHIP_RV630:
+       case CHIP_RV670:
+               /* todo */
+               break;
+       case CHIP_RV620:
+       case CHIP_RV635:
+       case CHIP_RS780:
+       case CHIP_RS880:
+       case CHIP_RV770:
+       case CHIP_RV730:
+       case CHIP_RV710:
+       case CHIP_RV740:
+               /* todo */
+               break;
+       case CHIP_CEDAR:
+       case CHIP_REDWOOD:
+       case CHIP_JUNIPER:
+       case CHIP_CYPRESS:
+       case CHIP_HEMLOCK:
+               /* todo */
+               break;
+       default:
+               DRM_ERROR("i2c: unhandled radeon chip\n");
+               break;
+       }
+       return prescale;
+}
+
+
+/* hw i2c engine for r1xx-4xx hardware
+ * hw can buffer up to 15 bytes
+ */
+static int r100_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
+                           struct i2c_msg *msgs, int num)
+{
+       struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
+       struct radeon_device *rdev = i2c->dev->dev_private;
+       struct radeon_i2c_bus_rec *rec = &i2c->rec;
+       struct i2c_msg *p;
+       int i, j, k, ret = num;
+       u32 prescale;
+       u32 i2c_cntl_0, i2c_cntl_1, i2c_data;
+       u32 tmp, reg;
+
+       mutex_lock(&rdev->dc_hw_i2c_mutex);
+       /* take the pm lock since we need a constant sclk */
+       mutex_lock(&rdev->pm.mutex);
+
+       prescale = radeon_get_i2c_prescale(rdev);
+
+       reg = ((prescale << RADEON_I2C_PRESCALE_SHIFT) |
+              RADEON_I2C_START |
+              RADEON_I2C_STOP |
+              RADEON_I2C_GO);
+
+       if (rdev->is_atom_bios) {
+               tmp = RREG32(RADEON_BIOS_6_SCRATCH);
+               WREG32(RADEON_BIOS_6_SCRATCH, tmp | ATOM_S6_HW_I2C_BUSY_STATE);
+       }
+
+       if (rec->mm_i2c) {
+               i2c_cntl_0 = RADEON_I2C_CNTL_0;
+               i2c_cntl_1 = RADEON_I2C_CNTL_1;
+               i2c_data = RADEON_I2C_DATA;
+       } else {
+               i2c_cntl_0 = RADEON_DVI_I2C_CNTL_0;
+               i2c_cntl_1 = RADEON_DVI_I2C_CNTL_1;
+               i2c_data = RADEON_DVI_I2C_DATA;
+
+               switch (rdev->family) {
+               case CHIP_R100:
+               case CHIP_RV100:
+               case CHIP_RS100:
+               case CHIP_RV200:
+               case CHIP_RS200:
+               case CHIP_RS300:
+                       switch (rec->mask_clk_reg) {
+                       case RADEON_GPIO_DVI_DDC:
+                               /* no gpio select bit */
+                               break;
+                       default:
+                               DRM_ERROR("gpio not supported with hw i2c\n");
+                               ret = -EINVAL;
+                               goto done;
+                       }
+                       break;
+               case CHIP_R200:
+                       /* only bit 4 on r200 */
+                       switch (rec->mask_clk_reg) {
+                       case RADEON_GPIO_DVI_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1);
+                               break;
+                       case RADEON_GPIO_MONID:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3);
+                               break;
+                       default:
+                               DRM_ERROR("gpio not supported with hw i2c\n");
+                               ret = -EINVAL;
+                               goto done;
+                       }
+                       break;
+               case CHIP_RV250:
+               case CHIP_RV280:
+                       /* bits 3 and 4 */
+                       switch (rec->mask_clk_reg) {
+                       case RADEON_GPIO_DVI_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1);
+                               break;
+                       case RADEON_GPIO_VGA_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC2);
+                               break;
+                       case RADEON_GPIO_CRT2_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3);
+                               break;
+                       default:
+                               DRM_ERROR("gpio not supported with hw i2c\n");
+                               ret = -EINVAL;
+                               goto done;
+                       }
+                       break;
+               case CHIP_R300:
+               case CHIP_R350:
+                       /* only bit 4 on r300/r350 */
+                       switch (rec->mask_clk_reg) {
+                       case RADEON_GPIO_VGA_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1);
+                               break;
+                       case RADEON_GPIO_DVI_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3);
+                               break;
+                       default:
+                               DRM_ERROR("gpio not supported with hw i2c\n");
+                               ret = -EINVAL;
+                               goto done;
+                       }
+                       break;
+               case CHIP_RV350:
+               case CHIP_RV380:
+               case CHIP_R420:
+               case CHIP_R423:
+               case CHIP_RV410:
+               case CHIP_RS400:
+               case CHIP_RS480:
+                       /* bits 3 and 4 */
+                       switch (rec->mask_clk_reg) {
+                       case RADEON_GPIO_VGA_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1);
+                               break;
+                       case RADEON_GPIO_DVI_DDC:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC2);
+                               break;
+                       case RADEON_GPIO_MONID:
+                               reg |= R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3);
+                               break;
+                       default:
+                               DRM_ERROR("gpio not supported with hw i2c\n");
+                               ret = -EINVAL;
+                               goto done;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unsupported asic\n");
+                       ret = -EINVAL;
+                       goto done;
+                       break;
+               }
+       }
+
+       /* check for bus probe */
+       p = &msgs[0];
+       if ((num == 1) && (p->len == 0)) {
+               WREG32(i2c_cntl_0, (RADEON_I2C_DONE |
+                                   RADEON_I2C_NACK |
+                                   RADEON_I2C_HALT |
+                                   RADEON_I2C_SOFT_RST));
+               WREG32(i2c_data, (p->addr << 1) & 0xff);
+               WREG32(i2c_data, 0);
+               WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) |
+                                   (1 << RADEON_I2C_ADDR_COUNT_SHIFT) |
+                                   RADEON_I2C_EN |
+                                   (48 << RADEON_I2C_TIME_LIMIT_SHIFT)));
+               WREG32(i2c_cntl_0, reg);
+               for (k = 0; k < 32; k++) {
+                       udelay(10);
+                       tmp = RREG32(i2c_cntl_0);
+                       if (tmp & RADEON_I2C_GO)
+                               continue;
+                       tmp = RREG32(i2c_cntl_0);
+                       if (tmp & RADEON_I2C_DONE)
+                               break;
+                       else {
+                               DRM_DEBUG("i2c write error 0x%08x\n", tmp);
+                               WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT);
+                               ret = -EIO;
+                               goto done;
+                       }
+               }
+               goto done;
+       }
+
+       for (i = 0; i < num; i++) {
+               p = &msgs[i];
+               for (j = 0; j < p->len; j++) {
+                       if (p->flags & I2C_M_RD) {
+                               WREG32(i2c_cntl_0, (RADEON_I2C_DONE |
+                                                   RADEON_I2C_NACK |
+                                                   RADEON_I2C_HALT |
+                                                   RADEON_I2C_SOFT_RST));
+                               WREG32(i2c_data, ((p->addr << 1) & 0xff) | 0x1);
+                               WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) |
+                                                   (1 << RADEON_I2C_ADDR_COUNT_SHIFT) |
+                                                   RADEON_I2C_EN |
+                                                   (48 << RADEON_I2C_TIME_LIMIT_SHIFT)));
+                               WREG32(i2c_cntl_0, reg | RADEON_I2C_RECEIVE);
+                               for (k = 0; k < 32; k++) {
+                                       udelay(10);
+                                       tmp = RREG32(i2c_cntl_0);
+                                       if (tmp & RADEON_I2C_GO)
+                                               continue;
+                                       tmp = RREG32(i2c_cntl_0);
+                                       if (tmp & RADEON_I2C_DONE)
+                                               break;
+                                       else {
+                                               DRM_DEBUG("i2c read error 0x%08x\n", tmp);
+                                               WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT);
+                                               ret = -EIO;
+                                               goto done;
+                                       }
+                               }
+                               p->buf[j] = RREG32(i2c_data) & 0xff;
+                       } else {
+                               WREG32(i2c_cntl_0, (RADEON_I2C_DONE |
+                                                   RADEON_I2C_NACK |
+                                                   RADEON_I2C_HALT |
+                                                   RADEON_I2C_SOFT_RST));
+                               WREG32(i2c_data, (p->addr << 1) & 0xff);
+                               WREG32(i2c_data, p->buf[j]);
+                               WREG32(i2c_cntl_1, ((1 << RADEON_I2C_DATA_COUNT_SHIFT) |
+                                                   (1 << RADEON_I2C_ADDR_COUNT_SHIFT) |
+                                                   RADEON_I2C_EN |
+                                                   (48 << RADEON_I2C_TIME_LIMIT_SHIFT)));
+                               WREG32(i2c_cntl_0, reg);
+                               for (k = 0; k < 32; k++) {
+                                       udelay(10);
+                                       tmp = RREG32(i2c_cntl_0);
+                                       if (tmp & RADEON_I2C_GO)
+                                               continue;
+                                       tmp = RREG32(i2c_cntl_0);
+                                       if (tmp & RADEON_I2C_DONE)
+                                               break;
+                                       else {
+                                               DRM_DEBUG("i2c write error 0x%08x\n", tmp);
+                                               WREG32(i2c_cntl_0, tmp | RADEON_I2C_ABORT);
+                                               ret = -EIO;
+                                               goto done;
+                                       }
+                               }
+                       }
+               }
+       }
+
+done:
+       WREG32(i2c_cntl_0, 0);
+       WREG32(i2c_cntl_1, 0);
+       WREG32(i2c_cntl_0, (RADEON_I2C_DONE |
+                           RADEON_I2C_NACK |
+                           RADEON_I2C_HALT |
+                           RADEON_I2C_SOFT_RST));
+
+       if (rdev->is_atom_bios) {
+               tmp = RREG32(RADEON_BIOS_6_SCRATCH);
+               tmp &= ~ATOM_S6_HW_I2C_BUSY_STATE;
+               WREG32(RADEON_BIOS_6_SCRATCH, tmp);
+       }
+
+       mutex_unlock(&rdev->pm.mutex);
+       mutex_unlock(&rdev->dc_hw_i2c_mutex);
+
+       return ret;
+}
+
+/* hw i2c engine for r5xx hardware
+ * hw can buffer up to 15 bytes
+ */
+static int r500_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
+                           struct i2c_msg *msgs, int num)
+{
+       struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
+       struct radeon_device *rdev = i2c->dev->dev_private;
+       struct radeon_i2c_bus_rec *rec = &i2c->rec;
+       struct i2c_msg *p;
+       int i, j, remaining, current_count, buffer_offset, ret = num;
+       u32 prescale;
+       u32 tmp, reg;
+       u32 saved1, saved2;
+
+       mutex_lock(&rdev->dc_hw_i2c_mutex);
+       /* take the pm lock since we need a constant sclk */
+       mutex_lock(&rdev->pm.mutex);
+
+       prescale = radeon_get_i2c_prescale(rdev);
+
+       /* clear gpio mask bits */
+       tmp = RREG32(rec->mask_clk_reg);
+       tmp &= ~rec->mask_clk_mask;
+       WREG32(rec->mask_clk_reg, tmp);
+       tmp = RREG32(rec->mask_clk_reg);
+
+       tmp = RREG32(rec->mask_data_reg);
+       tmp &= ~rec->mask_data_mask;
+       WREG32(rec->mask_data_reg, tmp);
+       tmp = RREG32(rec->mask_data_reg);
+
+       /* clear pin values */
+       tmp = RREG32(rec->a_clk_reg);
+       tmp &= ~rec->a_clk_mask;
+       WREG32(rec->a_clk_reg, tmp);
+       tmp = RREG32(rec->a_clk_reg);
+
+       tmp = RREG32(rec->a_data_reg);
+       tmp &= ~rec->a_data_mask;
+       WREG32(rec->a_data_reg, tmp);
+       tmp = RREG32(rec->a_data_reg);
+
+       /* set the pins to input */
+       tmp = RREG32(rec->en_clk_reg);
+       tmp &= ~rec->en_clk_mask;
+       WREG32(rec->en_clk_reg, tmp);
+       tmp = RREG32(rec->en_clk_reg);
+
+       tmp = RREG32(rec->en_data_reg);
+       tmp &= ~rec->en_data_mask;
+       WREG32(rec->en_data_reg, tmp);
+       tmp = RREG32(rec->en_data_reg);
+
+       /* */
+       tmp = RREG32(RADEON_BIOS_6_SCRATCH);
+       WREG32(RADEON_BIOS_6_SCRATCH, tmp | ATOM_S6_HW_I2C_BUSY_STATE);
+       saved1 = RREG32(AVIVO_DC_I2C_CONTROL1);
+       saved2 = RREG32(0x494);
+       WREG32(0x494, saved2 | 0x1);
+
+       WREG32(AVIVO_DC_I2C_ARBITRATION, AVIVO_DC_I2C_SW_WANTS_TO_USE_I2C);
+       for (i = 0; i < 50; i++) {
+               udelay(1);
+               if (RREG32(AVIVO_DC_I2C_ARBITRATION) & AVIVO_DC_I2C_SW_CAN_USE_I2C)
+                       break;
+       }
+       if (i == 50) {
+               DRM_ERROR("failed to get i2c bus\n");
+               ret = -EBUSY;
+               goto done;
+       }
+
+       reg = AVIVO_DC_I2C_START | AVIVO_DC_I2C_STOP | AVIVO_DC_I2C_EN;
+       switch (rec->mask_clk_reg) {
+       case AVIVO_DC_GPIO_DDC1_MASK:
+               reg |= AVIVO_DC_I2C_PIN_SELECT(AVIVO_SEL_DDC1);
+               break;
+       case AVIVO_DC_GPIO_DDC2_MASK:
+               reg |= AVIVO_DC_I2C_PIN_SELECT(AVIVO_SEL_DDC2);
+               break;
+       case AVIVO_DC_GPIO_DDC3_MASK:
+               reg |= AVIVO_DC_I2C_PIN_SELECT(AVIVO_SEL_DDC3);
+               break;
+       default:
+               DRM_ERROR("gpio not supported with hw i2c\n");
+               ret = -EINVAL;
+               goto done;
+       }
+
+       /* check for bus probe */
+       p = &msgs[0];
+       if ((num == 1) && (p->len == 0)) {
+               WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE |
+                                             AVIVO_DC_I2C_NACK |
+                                             AVIVO_DC_I2C_HALT));
+               WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET);
+               udelay(1);
+               WREG32(AVIVO_DC_I2C_RESET, 0);
+
+               WREG32(AVIVO_DC_I2C_DATA, (p->addr << 1) & 0xff);
+               WREG32(AVIVO_DC_I2C_DATA, 0);
+
+               WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48));
+               WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) |
+                                              AVIVO_DC_I2C_DATA_COUNT(1) |
+                                              (prescale << 16)));
+               WREG32(AVIVO_DC_I2C_CONTROL1, reg);
+               WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO);
+               for (j = 0; j < 200; j++) {
+                       udelay(50);
+                       tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+                       if (tmp & AVIVO_DC_I2C_GO)
+                               continue;
+                       tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+                       if (tmp & AVIVO_DC_I2C_DONE)
+                               break;
+                       else {
+                               DRM_DEBUG("i2c write error 0x%08x\n", tmp);
+                               WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT);
+                               ret = -EIO;
+                               goto done;
+                       }
+               }
+               goto done;
+       }
+
+       for (i = 0; i < num; i++) {
+               p = &msgs[i];
+               remaining = p->len;
+               buffer_offset = 0;
+               if (p->flags & I2C_M_RD) {
+                       while (remaining) {
+                               if (remaining > 15)
+                                       current_count = 15;
+                               else
+                                       current_count = remaining;
+                               WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE |
+                                                             AVIVO_DC_I2C_NACK |
+                                                             AVIVO_DC_I2C_HALT));
+                               WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET);
+                               udelay(1);
+                               WREG32(AVIVO_DC_I2C_RESET, 0);
+
+                               WREG32(AVIVO_DC_I2C_DATA, ((p->addr << 1) & 0xff) | 0x1);
+                               WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48));
+                               WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) |
+                                                              AVIVO_DC_I2C_DATA_COUNT(current_count) |
+                                                              (prescale << 16)));
+                               WREG32(AVIVO_DC_I2C_CONTROL1, reg | AVIVO_DC_I2C_RECEIVE);
+                               WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO);
+                               for (j = 0; j < 200; j++) {
+                                       udelay(50);
+                                       tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+                                       if (tmp & AVIVO_DC_I2C_GO)
+                                               continue;
+                                       tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+                                       if (tmp & AVIVO_DC_I2C_DONE)
+                                               break;
+                                       else {
+                                               DRM_DEBUG("i2c read error 0x%08x\n", tmp);
+                                               WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT);
+                                               ret = -EIO;
+                                               goto done;
+                                       }
+                               }
+                               for (j = 0; j < current_count; j++)
+                                       p->buf[buffer_offset + j] = RREG32(AVIVO_DC_I2C_DATA) & 0xff;
+                               remaining -= current_count;
+                               buffer_offset += current_count;
+                       }
+               } else {
+                       while (remaining) {
+                               if (remaining > 15)
+                                       current_count = 15;
+                               else
+                                       current_count = remaining;
+                               WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE |
+                                                             AVIVO_DC_I2C_NACK |
+                                                             AVIVO_DC_I2C_HALT));
+                               WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET);
+                               udelay(1);
+                               WREG32(AVIVO_DC_I2C_RESET, 0);
+
+                               WREG32(AVIVO_DC_I2C_DATA, (p->addr << 1) & 0xff);
+                               for (j = 0; j < current_count; j++)
+                                       WREG32(AVIVO_DC_I2C_DATA, p->buf[buffer_offset + j]);
+
+                               WREG32(AVIVO_DC_I2C_CONTROL3, AVIVO_DC_I2C_TIME_LIMIT(48));
+                               WREG32(AVIVO_DC_I2C_CONTROL2, (AVIVO_DC_I2C_ADDR_COUNT(1) |
+                                                              AVIVO_DC_I2C_DATA_COUNT(current_count) |
+                                                              (prescale << 16)));
+                               WREG32(AVIVO_DC_I2C_CONTROL1, reg);
+                               WREG32(AVIVO_DC_I2C_STATUS1, AVIVO_DC_I2C_GO);
+                               for (j = 0; j < 200; j++) {
+                                       udelay(50);
+                                       tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+                                       if (tmp & AVIVO_DC_I2C_GO)
+                                               continue;
+                                       tmp = RREG32(AVIVO_DC_I2C_STATUS1);
+                                       if (tmp & AVIVO_DC_I2C_DONE)
+                                               break;
+                                       else {
+                                               DRM_DEBUG("i2c write error 0x%08x\n", tmp);
+                                               WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_ABORT);
+                                               ret = -EIO;
+                                               goto done;
+                                       }
+                               }
+                               remaining -= current_count;
+                               buffer_offset += current_count;
+                       }
+               }
+       }
+
+done:
+       WREG32(AVIVO_DC_I2C_STATUS1, (AVIVO_DC_I2C_DONE |
+                                     AVIVO_DC_I2C_NACK |
+                                     AVIVO_DC_I2C_HALT));
+       WREG32(AVIVO_DC_I2C_RESET, AVIVO_DC_I2C_SOFT_RESET);
+       udelay(1);
+       WREG32(AVIVO_DC_I2C_RESET, 0);
+
+       WREG32(AVIVO_DC_I2C_ARBITRATION, AVIVO_DC_I2C_SW_DONE_USING_I2C);
+       WREG32(AVIVO_DC_I2C_CONTROL1, saved1);
+       WREG32(0x494, saved2);
+       tmp = RREG32(RADEON_BIOS_6_SCRATCH);
+       tmp &= ~ATOM_S6_HW_I2C_BUSY_STATE;
+       WREG32(RADEON_BIOS_6_SCRATCH, tmp);
+
+       mutex_unlock(&rdev->pm.mutex);
+       mutex_unlock(&rdev->dc_hw_i2c_mutex);
+
+       return ret;
+}
+
+static int radeon_sw_i2c_xfer(struct i2c_adapter *i2c_adap,
+                             struct i2c_msg *msgs, int num)
+{
+       struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
+       int ret;
+
+       radeon_i2c_do_lock(i2c, 1);
+       ret = i2c_transfer(&i2c->algo.radeon.bit_adapter, msgs, num);
+       radeon_i2c_do_lock(i2c, 0);
+
+       return ret;
+}
+
+static int radeon_i2c_xfer(struct i2c_adapter *i2c_adap,
+                          struct i2c_msg *msgs, int num)
+{
+       struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
+       struct radeon_device *rdev = i2c->dev->dev_private;
+       struct radeon_i2c_bus_rec *rec = &i2c->rec;
+       int ret;
+
+       switch (rdev->family) {
+       case CHIP_R100:
+       case CHIP_RV100:
+       case CHIP_RS100:
+       case CHIP_RV200:
+       case CHIP_RS200:
+       case CHIP_R200:
+       case CHIP_RV250:
+       case CHIP_RS300:
+       case CHIP_RV280:
+       case CHIP_R300:
+       case CHIP_R350:
+       case CHIP_RV350:
+       case CHIP_RV380:
+       case CHIP_R420:
+       case CHIP_R423:
+       case CHIP_RV410:
+       case CHIP_RS400:
+       case CHIP_RS480:
+               if (rec->hw_capable)
+                       ret = r100_hw_i2c_xfer(i2c_adap, msgs, num);
+               else
+                       ret = radeon_sw_i2c_xfer(i2c_adap, msgs, num);
+               break;
+       case CHIP_RS600:
+       case CHIP_RS690:
+       case CHIP_RS740:
+               /* XXX fill in hw i2c implementation */
+               ret = radeon_sw_i2c_xfer(i2c_adap, msgs, num);
+               break;
+       case CHIP_RV515:
+       case CHIP_R520:
+       case CHIP_RV530:
+       case CHIP_RV560:
+       case CHIP_RV570:
+       case CHIP_R580:
+               if (rec->hw_capable) {
+                       if (rec->mm_i2c)
+                               ret = r100_hw_i2c_xfer(i2c_adap, msgs, num);
+                       else
+                               ret = r500_hw_i2c_xfer(i2c_adap, msgs, num);
+               } else
+                       ret = radeon_sw_i2c_xfer(i2c_adap, msgs, num);
+               break;
+       case CHIP_R600:
+       case CHIP_RV610:
+       case CHIP_RV630:
+       case CHIP_RV670:
+               /* XXX fill in hw i2c implementation */
+               ret = radeon_sw_i2c_xfer(i2c_adap, msgs, num);
+               break;
+       case CHIP_RV620:
+       case CHIP_RV635:
+       case CHIP_RS780:
+       case CHIP_RS880:
+       case CHIP_RV770:
+       case CHIP_RV730:
+       case CHIP_RV710:
+       case CHIP_RV740:
+               /* XXX fill in hw i2c implementation */
+               ret = radeon_sw_i2c_xfer(i2c_adap, msgs, num);
+               break;
+       case CHIP_CEDAR:
+       case CHIP_REDWOOD:
+       case CHIP_JUNIPER:
+       case CHIP_CYPRESS:
+       case CHIP_HEMLOCK:
+               /* XXX fill in hw i2c implementation */
+               ret = radeon_sw_i2c_xfer(i2c_adap, msgs, num);
+               break;
+       default:
+               DRM_ERROR("i2c: unhandled radeon chip\n");
+               ret = -EIO;
+               break;
+       }
+
+       return ret;
+}
+
+static u32 radeon_i2c_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm radeon_i2c_algo = {
+       .master_xfer = radeon_i2c_xfer,
+       .functionality = radeon_i2c_func,
+};
+
 struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
                                          struct radeon_i2c_bus_rec *rec,
                                          const char *name)
@@ -179,23 +878,36 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
        if (i2c == NULL)
                return NULL;
 
-       i2c->adapter.owner = THIS_MODULE;
-       i2c->dev = dev;
-       i2c_set_adapdata(&i2c->adapter, i2c);
-       i2c->adapter.algo_data = &i2c->algo.bit;
-       i2c->algo.bit.setsda = set_data;
-       i2c->algo.bit.setscl = set_clock;
-       i2c->algo.bit.getsda = get_data;
-       i2c->algo.bit.getscl = get_clock;
-       i2c->algo.bit.udelay = 20;
+       /* set the internal bit adapter */
+       i2c->algo.radeon.bit_adapter.owner = THIS_MODULE;
+       i2c_set_adapdata(&i2c->algo.radeon.bit_adapter, i2c);
+       sprintf(i2c->algo.radeon.bit_adapter.name, "Radeon internal i2c bit bus %s", name);
+       i2c->algo.radeon.bit_adapter.algo_data = &i2c->algo.radeon.bit_data;
+       i2c->algo.radeon.bit_data.setsda = set_data;
+       i2c->algo.radeon.bit_data.setscl = set_clock;
+       i2c->algo.radeon.bit_data.getsda = get_data;
+       i2c->algo.radeon.bit_data.getscl = get_clock;
+       i2c->algo.radeon.bit_data.udelay = 20;
        /* vesa says 2.2 ms is enough, 1 jiffy doesn't seem to always
         * make this, 2 jiffies is a lot more reliable */
-       i2c->algo.bit.timeout = 2;
-       i2c->algo.bit.data = i2c;
+       i2c->algo.radeon.bit_data.timeout = 2;
+       i2c->algo.radeon.bit_data.data = i2c;
+       ret = i2c_bit_add_bus(&i2c->algo.radeon.bit_adapter);
+       if (ret) {
+               DRM_ERROR("Failed to register internal bit i2c %s\n", name);
+               goto out_free;
+       }
+       /* set the radeon i2c adapter */
+       i2c->dev = dev;
        i2c->rec = *rec;
-       ret = i2c_bit_add_bus(&i2c->adapter);
+       i2c->adapter.owner = THIS_MODULE;
+       i2c_set_adapdata(&i2c->adapter, i2c);
+       sprintf(i2c->adapter.name, "Radeon i2c %s", name);
+       i2c->adapter.algo_data = &i2c->algo.radeon;
+       i2c->adapter.algo = &radeon_i2c_algo;
+       ret = i2c_add_adapter(&i2c->adapter);
        if (ret) {
-               DRM_INFO("Failed to register i2c %s\n", name);
+               DRM_ERROR("Failed to register i2c %s\n", name);
                goto out_free;
        }
 
@@ -237,8 +949,16 @@ out_free:
 
 }
 
-
 void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
+{
+       if (!i2c)
+               return;
+       i2c_del_adapter(&i2c->algo.radeon.bit_adapter);
+       i2c_del_adapter(&i2c->adapter);
+       kfree(i2c);
+}
+
+void radeon_i2c_destroy_dp(struct radeon_i2c_chan *i2c)
 {
        if (!i2c)
                return;
@@ -252,10 +972,10 @@ struct drm_encoder *radeon_best_encoder(struct drm_connector *connector)
        return NULL;
 }
 
-void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
-                           u8 slave_addr,
-                           u8 addr,
-                           u8 *val)
+void radeon_i2c_get_byte(struct radeon_i2c_chan *i2c_bus,
+                        u8 slave_addr,
+                        u8 addr,
+                        u8 *val)
 {
        u8 out_buf[2];
        u8 in_buf[2];
@@ -286,10 +1006,10 @@ void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
        }
 }
 
-void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c_bus,
-                           u8 slave_addr,
-                           u8 addr,
-                           u8 val)
+void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus,
+                        u8 slave_addr,
+                        u8 addr,
+                        u8 val)
 {
        uint8_t out_buf[2];
        struct i2c_msg msg = {
index f23b05606eb53f6fbe2f79de24f9613e318683ce..20ec276e7596c1937a2a6a4beaceb1247bdfb085 100644 (file)
@@ -30,6 +30,8 @@
 #include "radeon.h"
 #include "radeon_drm.h"
 
+#include <linux/vga_switcheroo.h>
+
 int radeon_driver_unload_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
@@ -136,6 +138,7 @@ int radeon_driver_firstopen_kms(struct drm_device *dev)
 
 void radeon_driver_lastclose_kms(struct drm_device *dev)
 {
+       vga_switcheroo_process_delayed_switch();
 }
 
 int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
@@ -276,17 +279,17 @@ struct drm_ioctl_desc radeon_ioctls_kms[] = {
        DRM_IOCTL_DEF(DRM_RADEON_SURF_ALLOC, radeon_surface_alloc_kms, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_RADEON_SURF_FREE, radeon_surface_free_kms, DRM_AUTH),
        /* KMS */
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_INFO, radeon_gem_info_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_CREATE, radeon_gem_create_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_MMAP, radeon_gem_mmap_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_DOMAIN, radeon_gem_set_domain_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_PREAD, radeon_gem_pread_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_PWRITE, radeon_gem_pwrite_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_WAIT_IDLE, radeon_gem_wait_idle_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_CS, radeon_cs_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_INFO, radeon_info_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_TILING, radeon_gem_set_tiling_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH),
-       DRM_IOCTL_DEF(DRM_RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_INFO, radeon_gem_info_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_CREATE, radeon_gem_create_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_MMAP, radeon_gem_mmap_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_DOMAIN, radeon_gem_set_domain_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_PREAD, radeon_gem_pread_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_PWRITE, radeon_gem_pwrite_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_WAIT_IDLE, radeon_gem_wait_idle_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_CS, radeon_cs_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_INFO, radeon_info_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_TILING, radeon_gem_set_tiling_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH|DRM_UNLOCKED),
+       DRM_IOCTL_DEF(DRM_RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED),
 };
 int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms);
index b6d8081e124675842c7614cea33d35f5d8357912..df23d6a01d02297a8ee0832a9f800f48b4ea957c 100644 (file)
@@ -403,7 +403,7 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
 
        /* if scanout was in GTT this really wouldn't work */
        /* crtc offset is from display base addr not FB location */
-       radeon_crtc->legacy_display_base_addr = rdev->mc.vram_location;
+       radeon_crtc->legacy_display_base_addr = rdev->mc.vram_start;
 
        base -= radeon_crtc->legacy_display_base_addr;
 
@@ -582,29 +582,6 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
                                   ? RADEON_CRTC_V_SYNC_POL
                                   : 0));
 
-       /* TODO -> Dell Server */
-       if (0) {
-               uint32_t disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
-               uint32_t tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
-               uint32_t dac2_cntl = RREG32(RADEON_DAC_CNTL2);
-               uint32_t crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL);
-
-               dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
-               dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
-
-               /* For CRT on DAC2, don't turn it on if BIOS didn't
-                  enable it, even it's detected.
-               */
-               disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
-               tv_dac_cntl &= ~((1<<2) | (3<<8) | (7<<24) | (0xff<<16));
-               tv_dac_cntl |= (0x03 | (2<<8) | (0x58<<16));
-
-               WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
-               WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
-               WREG32(RADEON_DAC_CNTL2, dac2_cntl);
-               WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
-       }
-
        if (radeon_crtc->crtc_id) {
                uint32_t crtc2_gen_cntl;
                uint32_t disp2_merge_cntl;
@@ -726,6 +703,10 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
                pll = &rdev->clock.p1pll;
 
        pll->flags = RADEON_PLL_LEGACY;
+       if (radeon_new_pll == 1)
+               pll->algo = PLL_ALGO_NEW;
+       else
+               pll->algo = PLL_ALGO_LEGACY;
 
        if (mode->clock > 200000) /* range limits??? */
                pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
index 38e45e231ef5ae819298c51b9942c03bceda3059..cf389ce50a8a7ecb3db5a31a4829c45e690bbc20 100644 (file)
@@ -115,6 +115,9 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
                radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
        else
                radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+       /* adjust pm to dpms change */
+       radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder)
@@ -214,6 +217,11 @@ static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder,
                                     struct drm_display_mode *adjusted_mode)
 {
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+
+       /* adjust pm to upcoming mode change */
+       radeon_pm_compute_clocks(rdev);
 
        /* set the active encoder to connector routing */
        radeon_encoder_set_active_device(encoder);
@@ -285,6 +293,9 @@ static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode
                radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
        else
                radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+       /* adjust pm to dpms change */
+       radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder)
@@ -470,6 +481,9 @@ static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode)
                radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
        else
                radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+       /* adjust pm to dpms change */
+       radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder)
@@ -635,6 +649,9 @@ static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode)
                radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
        else
                radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+       /* adjust pm to dpms change */
+       radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder)
@@ -842,6 +859,9 @@ static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode)
                radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
        else
                radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+       /* adjust pm to dpms change */
+       radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder)
index e81b2aeb6a8fa28338aa1ea6d1b79374ee6a03e6..1702b820aa4d3137e0559d44bd64bc37d8c28d7c 100644 (file)
@@ -83,6 +83,8 @@ struct radeon_i2c_bus_rec {
        bool valid;
        /* id used by atom */
        uint8_t i2c_id;
+       /* id used by atom */
+       uint8_t hpd_id;
        /* can be used with hw i2c engine */
        bool hw_capable;
        /* uses multi-media i2c engine */
@@ -113,6 +115,7 @@ struct radeon_tmds_pll {
 
 #define RADEON_MAX_BIOS_CONNECTOR 16
 
+/* pll flags */
 #define RADEON_PLL_USE_BIOS_DIVS        (1 << 0)
 #define RADEON_PLL_NO_ODD_POST_DIV      (1 << 1)
 #define RADEON_PLL_USE_REF_DIV          (1 << 2)
@@ -127,6 +130,12 @@ struct radeon_tmds_pll {
 #define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11)
 #define RADEON_PLL_USE_POST_DIV         (1 << 12)
 
+/* pll algo */
+enum radeon_pll_algo {
+       PLL_ALGO_LEGACY,
+       PLL_ALGO_NEW
+};
+
 struct radeon_pll {
        /* reference frequency */
        uint32_t reference_freq;
@@ -157,6 +166,13 @@ struct radeon_pll {
 
        /* pll id */
        uint32_t id;
+       /* pll algo */
+       enum radeon_pll_algo algo;
+};
+
+struct i2c_algo_radeon_data {
+       struct i2c_adapter bit_adapter;
+       struct i2c_algo_bit_data bit_data;
 };
 
 struct radeon_i2c_chan {
@@ -164,7 +180,7 @@ struct radeon_i2c_chan {
        struct drm_device *dev;
        union {
                struct i2c_algo_dp_aux_data dp;
-               struct i2c_algo_bit_data bit;
+               struct i2c_algo_radeon_data radeon;
        } algo;
        struct radeon_i2c_bus_rec rec;
 };
@@ -193,7 +209,7 @@ struct radeon_mode_info {
        struct card_info *atom_card_info;
        enum radeon_connector_table connector_table;
        bool mode_config_initialized;
-       struct radeon_crtc *crtcs[2];
+       struct radeon_crtc *crtcs[6];
        /* DVI-I properties */
        struct drm_property *coherent_mode_property;
        /* DAC enable load detect */
@@ -202,7 +218,8 @@ struct radeon_mode_info {
        struct drm_property *tv_std_property;
        /* legacy TMDS PLL detect */
        struct drm_property *tmds_pll_property;
-
+       /* hardcoded DFP edid from BIOS */
+       struct edid *bios_hardcoded_edid;
 };
 
 #define MAX_H_CODE_TIMING_LEN 32
@@ -237,6 +254,7 @@ struct radeon_crtc {
        fixed20_12 vsc;
        fixed20_12 hsc;
        struct drm_display_mode native_mode;
+       int pll_id;
 };
 
 struct radeon_encoder_primary_dac {
@@ -303,6 +321,7 @@ struct radeon_encoder_atom_dig {
        /* atom lvds */
        uint32_t lvds_misc;
        uint16_t panel_pwr_delay;
+       enum radeon_pll_algo pll_algo;
        struct radeon_atom_ss *ss;
        /* panel mode */
        struct drm_display_mode native_mode;
@@ -398,6 +417,7 @@ extern void dp_link_train(struct drm_encoder *encoder,
                          struct drm_connector *connector);
 extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
 extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
+extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action);
 extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
                                           int action, uint8_t lane_num,
                                           uint8_t lane_set);
@@ -411,14 +431,15 @@ extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
                                                 struct radeon_i2c_bus_rec *rec,
                                                 const char *name);
 extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c);
-extern void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
-                                  u8 slave_addr,
-                                  u8 addr,
-                                  u8 *val);
-extern void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c,
-                                  u8 slave_addr,
-                                  u8 addr,
-                                  u8 val);
+extern void radeon_i2c_destroy_dp(struct radeon_i2c_chan *i2c);
+extern void radeon_i2c_get_byte(struct radeon_i2c_chan *i2c_bus,
+                               u8 slave_addr,
+                               u8 addr,
+                               u8 *val);
+extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c,
+                               u8 slave_addr,
+                               u8 addr,
+                               u8 val);
 extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
 extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
 
@@ -432,14 +453,6 @@ extern void radeon_compute_pll(struct radeon_pll *pll,
                               uint32_t *ref_div_p,
                               uint32_t *post_div_p);
 
-extern void radeon_compute_pll_avivo(struct radeon_pll *pll,
-                                    uint64_t freq,
-                                    uint32_t *dot_clock_p,
-                                    uint32_t *fb_div_p,
-                                    uint32_t *frac_fb_div_p,
-                                    uint32_t *ref_div_p,
-                                    uint32_t *post_div_p);
-
 extern void radeon_setup_encoder_clones(struct drm_device *dev);
 
 struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index);
@@ -473,6 +486,9 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
 extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
                                   int x, int y);
 
+extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
+extern struct edid *
+radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);
 extern bool radeon_atom_get_clock_info(struct drm_device *dev);
 extern bool radeon_combios_get_clock_info(struct drm_device *dev);
 extern struct radeon_encoder_atom_dig *
@@ -531,7 +547,6 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
                               struct radeon_crtc *radeon_crtc);
 void radeon_legacy_init_crtc(struct drm_device *dev,
                             struct radeon_crtc *radeon_crtc);
-extern void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state);
 
 void radeon_get_clock_info(struct drm_device *dev);
 
index f1da370928eb7656d5f9fc474fd8cfceb3222ae2..fc9d00ac6b15ff68494c437dd758aff4f3214f66 100644 (file)
@@ -178,7 +178,6 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
 {
        int r, i;
 
-       radeon_ttm_placement_from_domain(bo, domain);
        if (bo->pin_count) {
                bo->pin_count++;
                if (gpu_addr)
@@ -186,6 +185,8 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
                return 0;
        }
        radeon_ttm_placement_from_domain(bo, domain);
+       /* force to pin into visible video ram */
+       bo->placement.lpfn = bo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
        for (i = 0; i < bo->placement.num_placement; i++)
                bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
        r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
index 8bce64cdc320f1028bd91231def13b45631de3e3..d4d1c39a0e99e4e8636dd9e1d095da4473c61e87 100644 (file)
  * OTHER DEALINGS IN THE SOFTWARE.
  *
  * Authors: RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com>
+ *          Alex Deucher <alexdeucher@gmail.com>
  */
 #include "drmP.h"
 #include "radeon.h"
+#include "avivod.h"
 
-int radeon_debugfs_pm_init(struct radeon_device *rdev);
+#define RADEON_IDLE_LOOP_MS 100
+#define RADEON_RECLOCK_DELAY_MS 200
+#define RADEON_WAIT_VBLANK_TIMEOUT 200
+
+static void radeon_pm_set_clocks_locked(struct radeon_device *rdev);
+static void radeon_pm_set_clocks(struct radeon_device *rdev);
+static void radeon_pm_idle_work_handler(struct work_struct *work);
+static int radeon_debugfs_pm_init(struct radeon_device *rdev);
+
+static const char *pm_state_names[4] = {
+       "PM_STATE_DISABLED",
+       "PM_STATE_MINIMUM",
+       "PM_STATE_PAUSED",
+       "PM_STATE_ACTIVE"
+};
+
+static const char *pm_state_types[5] = {
+       "Default",
+       "Powersave",
+       "Battery",
+       "Balanced",
+       "Performance",
+};
+
+static void radeon_print_power_mode_info(struct radeon_device *rdev)
+{
+       int i, j;
+       bool is_default;
+
+       DRM_INFO("%d Power State(s)\n", rdev->pm.num_power_states);
+       for (i = 0; i < rdev->pm.num_power_states; i++) {
+               if (rdev->pm.default_power_state == &rdev->pm.power_state[i])
+                       is_default = true;
+               else
+                       is_default = false;
+               DRM_INFO("State %d %s %s\n", i,
+                        pm_state_types[rdev->pm.power_state[i].type],
+                        is_default ? "(default)" : "");
+               if ((rdev->flags & RADEON_IS_PCIE) && !(rdev->flags & RADEON_IS_IGP))
+                       DRM_INFO("\t%d PCIE Lanes\n", rdev->pm.power_state[i].non_clock_info.pcie_lanes);
+               DRM_INFO("\t%d Clock Mode(s)\n", rdev->pm.power_state[i].num_clock_modes);
+               for (j = 0; j < rdev->pm.power_state[i].num_clock_modes; j++) {
+                       if (rdev->flags & RADEON_IS_IGP)
+                               DRM_INFO("\t\t%d engine: %d\n",
+                                        j,
+                                        rdev->pm.power_state[i].clock_info[j].sclk * 10);
+                       else
+                               DRM_INFO("\t\t%d engine/memory: %d/%d\n",
+                                        j,
+                                        rdev->pm.power_state[i].clock_info[j].sclk * 10,
+                                        rdev->pm.power_state[i].clock_info[j].mclk * 10);
+               }
+       }
+}
+
+static struct radeon_power_state * radeon_pick_power_state(struct radeon_device *rdev,
+                                                          enum radeon_pm_state_type type)
+{
+       int i, j;
+       enum radeon_pm_state_type wanted_types[2];
+       int wanted_count;
+
+       switch (type) {
+       case POWER_STATE_TYPE_DEFAULT:
+       default:
+               return rdev->pm.default_power_state;
+       case POWER_STATE_TYPE_POWERSAVE:
+               if (rdev->flags & RADEON_IS_MOBILITY) {
+                       wanted_types[0] = POWER_STATE_TYPE_POWERSAVE;
+                       wanted_types[1] = POWER_STATE_TYPE_BATTERY;
+                       wanted_count = 2;
+               } else {
+                       wanted_types[0] = POWER_STATE_TYPE_PERFORMANCE;
+                       wanted_count = 1;
+               }
+               break;
+       case POWER_STATE_TYPE_BATTERY:
+               if (rdev->flags & RADEON_IS_MOBILITY) {
+                       wanted_types[0] = POWER_STATE_TYPE_BATTERY;
+                       wanted_types[1] = POWER_STATE_TYPE_POWERSAVE;
+                       wanted_count = 2;
+               } else {
+                       wanted_types[0] = POWER_STATE_TYPE_PERFORMANCE;
+                       wanted_count = 1;
+               }
+               break;
+       case POWER_STATE_TYPE_BALANCED:
+       case POWER_STATE_TYPE_PERFORMANCE:
+               wanted_types[0] = type;
+               wanted_count = 1;
+               break;
+       }
+
+       for (i = 0; i < wanted_count; i++) {
+               for (j = 0; j < rdev->pm.num_power_states; j++) {
+                       if (rdev->pm.power_state[j].type == wanted_types[i])
+                               return &rdev->pm.power_state[j];
+               }
+       }
+
+       return rdev->pm.default_power_state;
+}
+
+static struct radeon_pm_clock_info * radeon_pick_clock_mode(struct radeon_device *rdev,
+                                                           struct radeon_power_state *power_state,
+                                                           enum radeon_pm_clock_mode_type type)
+{
+       switch (type) {
+       case POWER_MODE_TYPE_DEFAULT:
+       default:
+               return power_state->default_clock_mode;
+       case POWER_MODE_TYPE_LOW:
+               return &power_state->clock_info[0];
+       case POWER_MODE_TYPE_MID:
+               if (power_state->num_clock_modes > 2)
+                       return &power_state->clock_info[1];
+               else
+                       return &power_state->clock_info[0];
+               break;
+       case POWER_MODE_TYPE_HIGH:
+               return &power_state->clock_info[power_state->num_clock_modes - 1];
+       }
+
+}
+
+static void radeon_get_power_state(struct radeon_device *rdev,
+                                  enum radeon_pm_action action)
+{
+       switch (action) {
+       case PM_ACTION_MINIMUM:
+               rdev->pm.requested_power_state = radeon_pick_power_state(rdev, POWER_STATE_TYPE_BATTERY);
+               rdev->pm.requested_clock_mode =
+                       radeon_pick_clock_mode(rdev, rdev->pm.requested_power_state, POWER_MODE_TYPE_LOW);
+               break;
+       case PM_ACTION_DOWNCLOCK:
+               rdev->pm.requested_power_state = radeon_pick_power_state(rdev, POWER_STATE_TYPE_POWERSAVE);
+               rdev->pm.requested_clock_mode =
+                       radeon_pick_clock_mode(rdev, rdev->pm.requested_power_state, POWER_MODE_TYPE_MID);
+               break;
+       case PM_ACTION_UPCLOCK:
+               rdev->pm.requested_power_state = radeon_pick_power_state(rdev, POWER_STATE_TYPE_DEFAULT);
+               rdev->pm.requested_clock_mode =
+                       radeon_pick_clock_mode(rdev, rdev->pm.requested_power_state, POWER_MODE_TYPE_HIGH);
+               break;
+       case PM_ACTION_NONE:
+       default:
+               DRM_ERROR("Requested mode for not defined action\n");
+               return;
+       }
+       DRM_INFO("Requested: e: %d m: %d p: %d\n",
+                rdev->pm.requested_clock_mode->sclk,
+                rdev->pm.requested_clock_mode->mclk,
+                rdev->pm.requested_power_state->non_clock_info.pcie_lanes);
+}
+
+static void radeon_set_power_state(struct radeon_device *rdev)
+{
+       /* if *_clock_mode are the same, *_power_state are as well */
+       if (rdev->pm.requested_clock_mode == rdev->pm.current_clock_mode)
+               return;
+
+       DRM_INFO("Setting: e: %d m: %d p: %d\n",
+                rdev->pm.requested_clock_mode->sclk,
+                rdev->pm.requested_clock_mode->mclk,
+                rdev->pm.requested_power_state->non_clock_info.pcie_lanes);
+       /* set pcie lanes */
+       /* set voltage */
+       /* set engine clock */
+       radeon_set_engine_clock(rdev, rdev->pm.requested_clock_mode->sclk);
+       /* set memory clock */
+
+       rdev->pm.current_power_state = rdev->pm.requested_power_state;
+       rdev->pm.current_clock_mode = rdev->pm.requested_clock_mode;
+}
 
 int radeon_pm_init(struct radeon_device *rdev)
 {
+       rdev->pm.state = PM_STATE_DISABLED;
+       rdev->pm.planned_action = PM_ACTION_NONE;
+       rdev->pm.downclocked = false;
+
+       if (rdev->bios) {
+               if (rdev->is_atom_bios)
+                       radeon_atombios_get_power_modes(rdev);
+               else
+                       radeon_combios_get_power_modes(rdev);
+               radeon_print_power_mode_info(rdev);
+       }
+
        if (radeon_debugfs_pm_init(rdev)) {
                DRM_ERROR("Failed to register debugfs file for PM!\n");
        }
 
+       INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler);
+
+       if (radeon_dynpm != -1 && radeon_dynpm) {
+               rdev->pm.state = PM_STATE_PAUSED;
+               DRM_INFO("radeon: dynamic power management enabled\n");
+       }
+
+       DRM_INFO("radeon: power management initialized\n");
+
        return 0;
 }
 
+void radeon_pm_compute_clocks(struct radeon_device *rdev)
+{
+       struct drm_device *ddev = rdev->ddev;
+       struct drm_connector *connector;
+       struct radeon_crtc *radeon_crtc;
+       int count = 0;
+
+       if (rdev->pm.state == PM_STATE_DISABLED)
+               return;
+
+       mutex_lock(&rdev->pm.mutex);
+
+       rdev->pm.active_crtcs = 0;
+       list_for_each_entry(connector,
+               &ddev->mode_config.connector_list, head) {
+               if (connector->encoder &&
+                       connector->dpms != DRM_MODE_DPMS_OFF) {
+                       radeon_crtc = to_radeon_crtc(connector->encoder->crtc);
+                       rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id);
+                       ++count;
+               }
+       }
+
+       if (count > 1) {
+               if (rdev->pm.state == PM_STATE_ACTIVE) {
+                       cancel_delayed_work(&rdev->pm.idle_work);
+
+                       rdev->pm.state = PM_STATE_PAUSED;
+                       rdev->pm.planned_action = PM_ACTION_UPCLOCK;
+                       if (rdev->pm.downclocked)
+                               radeon_pm_set_clocks(rdev);
+
+                       DRM_DEBUG("radeon: dynamic power management deactivated\n");
+               }
+       } else if (count == 1) {
+               /* TODO: Increase clocks if needed for current mode */
+
+               if (rdev->pm.state == PM_STATE_MINIMUM) {
+                       rdev->pm.state = PM_STATE_ACTIVE;
+                       rdev->pm.planned_action = PM_ACTION_UPCLOCK;
+                       radeon_pm_set_clocks(rdev);
+
+                       queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+                               msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+               }
+               else if (rdev->pm.state == PM_STATE_PAUSED) {
+                       rdev->pm.state = PM_STATE_ACTIVE;
+                       queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+                               msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+                       DRM_DEBUG("radeon: dynamic power management activated\n");
+               }
+       }
+       else { /* count == 0 */
+               if (rdev->pm.state != PM_STATE_MINIMUM) {
+                       cancel_delayed_work(&rdev->pm.idle_work);
+
+                       rdev->pm.state = PM_STATE_MINIMUM;
+                       rdev->pm.planned_action = PM_ACTION_MINIMUM;
+                       radeon_pm_set_clocks(rdev);
+               }
+       }
+
+       mutex_unlock(&rdev->pm.mutex);
+}
+
+static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish)
+{
+       u32 stat_crtc1 = 0, stat_crtc2 = 0;
+       bool in_vbl = true;
+
+       if (ASIC_IS_AVIVO(rdev)) {
+               if (rdev->pm.active_crtcs & (1 << 0)) {
+                       stat_crtc1 = RREG32(D1CRTC_STATUS);
+                       if (!(stat_crtc1 & 1))
+                               in_vbl = false;
+               }
+               if (rdev->pm.active_crtcs & (1 << 1)) {
+                       stat_crtc2 = RREG32(D2CRTC_STATUS);
+                       if (!(stat_crtc2 & 1))
+                               in_vbl = false;
+               }
+       }
+       if (in_vbl == false)
+               DRM_INFO("not in vbl for pm change %08x %08x at %s\n", stat_crtc1,
+                        stat_crtc2, finish ? "exit" : "entry");
+       return in_vbl;
+}
+static void radeon_pm_set_clocks_locked(struct radeon_device *rdev)
+{
+       /*radeon_fence_wait_last(rdev);*/
+       switch (rdev->pm.planned_action) {
+       case PM_ACTION_UPCLOCK:
+               rdev->pm.downclocked = false;
+               break;
+       case PM_ACTION_DOWNCLOCK:
+               rdev->pm.downclocked = true;
+               break;
+       case PM_ACTION_MINIMUM:
+               break;
+       case PM_ACTION_NONE:
+               DRM_ERROR("%s: PM_ACTION_NONE\n", __func__);
+               break;
+       }
+
+       /* check if we are in vblank */
+       radeon_pm_debug_check_in_vbl(rdev, false);
+       radeon_set_power_state(rdev);
+       radeon_pm_debug_check_in_vbl(rdev, true);
+       rdev->pm.planned_action = PM_ACTION_NONE;
+}
+
+static void radeon_pm_set_clocks(struct radeon_device *rdev)
+{
+       radeon_get_power_state(rdev, rdev->pm.planned_action);
+       mutex_lock(&rdev->cp.mutex);
+
+       if (rdev->pm.active_crtcs & (1 << 0)) {
+               rdev->pm.req_vblank |= (1 << 0);
+               drm_vblank_get(rdev->ddev, 0);
+       }
+       if (rdev->pm.active_crtcs & (1 << 1)) {
+               rdev->pm.req_vblank |= (1 << 1);
+               drm_vblank_get(rdev->ddev, 1);
+       }
+       if (rdev->pm.active_crtcs)
+               wait_event_interruptible_timeout(
+                       rdev->irq.vblank_queue, 0,
+                       msecs_to_jiffies(RADEON_WAIT_VBLANK_TIMEOUT));
+       if (rdev->pm.req_vblank & (1 << 0)) {
+               rdev->pm.req_vblank &= ~(1 << 0);
+               drm_vblank_put(rdev->ddev, 0);
+       }
+       if (rdev->pm.req_vblank & (1 << 1)) {
+               rdev->pm.req_vblank &= ~(1 << 1);
+               drm_vblank_put(rdev->ddev, 1);
+       }
+
+       radeon_pm_set_clocks_locked(rdev);
+       mutex_unlock(&rdev->cp.mutex);
+}
+
+static void radeon_pm_idle_work_handler(struct work_struct *work)
+{
+       struct radeon_device *rdev;
+       rdev = container_of(work, struct radeon_device,
+                               pm.idle_work.work);
+
+       mutex_lock(&rdev->pm.mutex);
+       if (rdev->pm.state == PM_STATE_ACTIVE) {
+               unsigned long irq_flags;
+               int not_processed = 0;
+
+               read_lock_irqsave(&rdev->fence_drv.lock, irq_flags);
+               if (!list_empty(&rdev->fence_drv.emited)) {
+                       struct list_head *ptr;
+                       list_for_each(ptr, &rdev->fence_drv.emited) {
+                               /* count up to 3, that's enought info */
+                               if (++not_processed >= 3)
+                                       break;
+                       }
+               }
+               read_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
+
+               if (not_processed >= 3) { /* should upclock */
+                       if (rdev->pm.planned_action == PM_ACTION_DOWNCLOCK) {
+                               rdev->pm.planned_action = PM_ACTION_NONE;
+                       } else if (rdev->pm.planned_action == PM_ACTION_NONE &&
+                               rdev->pm.downclocked) {
+                               rdev->pm.planned_action =
+                                       PM_ACTION_UPCLOCK;
+                               rdev->pm.action_timeout = jiffies +
+                               msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
+                       }
+               } else if (not_processed == 0) { /* should downclock */
+                       if (rdev->pm.planned_action == PM_ACTION_UPCLOCK) {
+                               rdev->pm.planned_action = PM_ACTION_NONE;
+                       } else if (rdev->pm.planned_action == PM_ACTION_NONE &&
+                               !rdev->pm.downclocked) {
+                               rdev->pm.planned_action =
+                                       PM_ACTION_DOWNCLOCK;
+                               rdev->pm.action_timeout = jiffies +
+                               msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
+                       }
+               }
+
+               if (rdev->pm.planned_action != PM_ACTION_NONE &&
+                   jiffies > rdev->pm.action_timeout) {
+                       radeon_pm_set_clocks(rdev);
+               }
+       }
+       mutex_unlock(&rdev->pm.mutex);
+
+       queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+                                       msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+}
+
 /*
  * Debugfs info
  */
@@ -44,11 +436,14 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
        struct drm_device *dev = node->minor->dev;
        struct radeon_device *rdev = dev->dev_private;
 
+       seq_printf(m, "state: %s\n", pm_state_names[rdev->pm.state]);
        seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk);
        seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
        seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk);
        if (rdev->asic->get_memory_clock)
                seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
+       if (rdev->asic->get_pcie_lanes)
+               seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
 
        return 0;
 }
@@ -58,7 +453,7 @@ static struct drm_info_list radeon_pm_info_list[] = {
 };
 #endif
 
-int radeon_debugfs_pm_init(struct radeon_device *rdev)
+static int radeon_debugfs_pm_init(struct radeon_device *rdev)
 {
 #if defined(CONFIG_DEBUG_FS)
        return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list));
index 6d0a009dd4a11fff1bd62a85a0a24df4526c4d30..5c0dc082d3307752a7dbf7067ea1b98f5cafaafa 100644 (file)
@@ -54,7 +54,7 @@
 #include "r300_reg.h"
 #include "r500_reg.h"
 #include "r600_reg.h"
-
+#include "evergreen_reg.h"
 
 #define RADEON_MC_AGP_LOCATION         0x014c
 #define                RADEON_MC_AGP_START_MASK        0x0000FFFF
 
        /* Multimedia I2C bus */
 #define RADEON_I2C_CNTL_0                  0x0090
-#define RADEON_I2C_DONE                     (1 << 0)
-#define RADEON_I2C_NACK                     (1 << 1)
-#define RADEON_I2C_HALT                     (1 << 2)
-#define RADEON_I2C_SOFT_RST                 (1 << 5)
-#define RADEON_I2C_DRIVE_EN                 (1 << 6)
-#define RADEON_I2C_DRIVE_SEL                (1 << 7)
-#define RADEON_I2C_START                    (1 << 8)
-#define RADEON_I2C_STOP                     (1 << 9)
-#define RADEON_I2C_RECEIVE                  (1 << 10)
-#define RADEON_I2C_ABORT                    (1 << 11)
-#define RADEON_I2C_GO                       (1 << 12)
-#define RADEON_I2C_PRESCALE_SHIFT           16
+#       define RADEON_I2C_DONE              (1 << 0)
+#       define RADEON_I2C_NACK              (1 << 1)
+#       define RADEON_I2C_HALT              (1 << 2)
+#       define RADEON_I2C_SOFT_RST          (1 << 5)
+#       define RADEON_I2C_DRIVE_EN          (1 << 6)
+#       define RADEON_I2C_DRIVE_SEL         (1 << 7)
+#       define RADEON_I2C_START             (1 << 8)
+#       define RADEON_I2C_STOP              (1 << 9)
+#       define RADEON_I2C_RECEIVE           (1 << 10)
+#       define RADEON_I2C_ABORT             (1 << 11)
+#       define RADEON_I2C_GO                (1 << 12)
+#       define RADEON_I2C_PRESCALE_SHIFT    16
 #define RADEON_I2C_CNTL_1                   0x0094
-#define RADEON_I2C_DATA_COUNT_SHIFT         0
-#define RADEON_I2C_ADDR_COUNT_SHIFT         4
-#define RADEON_I2C_INTRA_BYTE_DELAY_SHIFT   8
-#define RADEON_I2C_SEL                      (1 << 16)
-#define RADEON_I2C_EN                       (1 << 17)
-#define RADEON_I2C_TIME_LIMIT_SHIFT         24
+#       define RADEON_I2C_DATA_COUNT_SHIFT  0
+#       define RADEON_I2C_ADDR_COUNT_SHIFT  4
+#       define RADEON_I2C_INTRA_BYTE_DELAY_SHIFT   8
+#       define RADEON_I2C_SEL               (1 << 16)
+#       define RADEON_I2C_EN                (1 << 17)
+#       define RADEON_I2C_TIME_LIMIT_SHIFT  24
 #define RADEON_I2C_DATA                            0x0098
 
 #define RADEON_DVI_I2C_CNTL_0              0x02e0
 #       define R200_DVI_I2C_PIN_SEL(x)      ((x) << 3)
-#       define R200_SEL_DDC1                0 /* 0x60 - VGA_DDC */
-#       define R200_SEL_DDC2                1 /* 0x64 - DVI_DDC */
-#       define R200_SEL_DDC3                2 /* 0x68 - MONID_DDC */
+#       define R200_SEL_DDC1                0 /* depends on asic */
+#       define R200_SEL_DDC2                1 /* depends on asic */
+#       define R200_SEL_DDC3                2 /* depends on asic */
+#      define RADEON_SW_WANTS_TO_USE_DVI_I2C (1 << 13)
+#      define RADEON_SW_CAN_USE_DVI_I2C      (1 << 13)
+#      define RADEON_SW_DONE_USING_DVI_I2C   (1 << 14)
+#      define RADEON_HW_NEEDS_DVI_I2C        (1 << 14)
+#      define RADEON_ABORT_HW_DVI_I2C        (1 << 15)
+#      define RADEON_HW_USING_DVI_I2C        (1 << 15)
 #define RADEON_DVI_I2C_CNTL_1               0x02e4
 #define RADEON_DVI_I2C_DATA                0x02e8
 
index 6579eb4c1f287007a3f22179c4593db621d12228..e50513a627351983edbea71e2d369e959434e069 100644 (file)
 
 int radeon_debugfs_ib_init(struct radeon_device *rdev);
 
+void radeon_ib_bogus_cleanup(struct radeon_device *rdev)
+{
+       struct radeon_ib *ib, *n;
+
+       list_for_each_entry_safe(ib, n, &rdev->ib_pool.bogus_ib, list) {
+               list_del(&ib->list);
+               vfree(ib->ptr);
+               kfree(ib);
+       }
+}
+
+void radeon_ib_bogus_add(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+       struct radeon_ib *bib;
+
+       bib = kmalloc(sizeof(*bib), GFP_KERNEL);
+       if (bib == NULL)
+               return;
+       bib->ptr = vmalloc(ib->length_dw * 4);
+       if (bib->ptr == NULL) {
+               kfree(bib);
+               return;
+       }
+       memcpy(bib->ptr, ib->ptr, ib->length_dw * 4);
+       bib->length_dw = ib->length_dw;
+       mutex_lock(&rdev->ib_pool.mutex);
+       list_add_tail(&bib->list, &rdev->ib_pool.bogus_ib);
+       mutex_unlock(&rdev->ib_pool.mutex);
+}
+
 /*
  * IB.
  */
@@ -142,6 +172,7 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
 
        if (rdev->ib_pool.robj)
                return 0;
+       INIT_LIST_HEAD(&rdev->ib_pool.bogus_ib);
        /* Allocate 1M object buffer */
        r = radeon_bo_create(rdev, NULL,  RADEON_IB_POOL_SIZE*64*1024,
                                true, RADEON_GEM_DOMAIN_GTT,
@@ -192,6 +223,8 @@ void radeon_ib_pool_fini(struct radeon_device *rdev)
                return;
        }
        mutex_lock(&rdev->ib_pool.mutex);
+       radeon_ib_bogus_cleanup(rdev);
+
        if (rdev->ib_pool.robj) {
                r = radeon_bo_reserve(rdev->ib_pool.robj, false);
                if (likely(r == 0)) {
@@ -349,15 +382,49 @@ static int radeon_debugfs_ib_info(struct seq_file *m, void *data)
        return 0;
 }
 
+static int radeon_debugfs_ib_bogus_info(struct seq_file *m, void *data)
+{
+       struct drm_info_node *node = (struct drm_info_node *) m->private;
+       struct radeon_device *rdev = node->info_ent->data;
+       struct radeon_ib *ib;
+       unsigned i;
+
+       mutex_lock(&rdev->ib_pool.mutex);
+       if (list_empty(&rdev->ib_pool.bogus_ib)) {
+               mutex_unlock(&rdev->ib_pool.mutex);
+               seq_printf(m, "no bogus IB recorded\n");
+               return 0;
+       }
+       ib = list_first_entry(&rdev->ib_pool.bogus_ib, struct radeon_ib, list);
+       list_del_init(&ib->list);
+       mutex_unlock(&rdev->ib_pool.mutex);
+       seq_printf(m, "IB size %05u dwords\n", ib->length_dw);
+       for (i = 0; i < ib->length_dw; i++) {
+               seq_printf(m, "[%05u]=0x%08X\n", i, ib->ptr[i]);
+       }
+       vfree(ib->ptr);
+       kfree(ib);
+       return 0;
+}
+
 static struct drm_info_list radeon_debugfs_ib_list[RADEON_IB_POOL_SIZE];
 static char radeon_debugfs_ib_names[RADEON_IB_POOL_SIZE][32];
+
+static struct drm_info_list radeon_debugfs_ib_bogus_info_list[] = {
+       {"radeon_ib_bogus", radeon_debugfs_ib_bogus_info, 0, NULL},
+};
 #endif
 
 int radeon_debugfs_ib_init(struct radeon_device *rdev)
 {
 #if defined(CONFIG_DEBUG_FS)
        unsigned i;
+       int r;
 
+       radeon_debugfs_ib_bogus_info_list[0].data = rdev;
+       r = radeon_debugfs_add_files(rdev, radeon_debugfs_ib_bogus_info_list, 1);
+       if (r)
+               return r;
        for (i = 0; i < RADEON_IB_POOL_SIZE; i++) {
                sprintf(radeon_debugfs_ib_names[i], "radeon_ib_%04u", i);
                radeon_debugfs_ib_list[i].name = radeon_debugfs_ib_names[i];
index 067167cb39cafa8444b2358cde28d5bc9e96f167..3c32f840dcd2294b3ee5d0af9407f21c9354209e 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "drmP.h"
 #include "drm.h"
+#include "drm_buffer.h"
 #include "drm_sarea.h"
 #include "radeon_drm.h"
 #include "radeon_drv.h"
@@ -91,21 +92,27 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
 static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
                                                     dev_priv,
                                                     struct drm_file *file_priv,
-                                                    int id, u32 *data)
+                                                    int id, struct drm_buffer *buf)
 {
+       u32 *data;
        switch (id) {
 
        case RADEON_EMIT_PP_MISC:
-               if (radeon_check_and_fixup_offset(dev_priv, file_priv,
-                   &data[(RADEON_RB3D_DEPTHOFFSET - RADEON_PP_MISC) / 4])) {
+               data = drm_buffer_pointer_to_dword(buf,
+                       (RADEON_RB3D_DEPTHOFFSET - RADEON_PP_MISC) / 4);
+
+               if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) {
                        DRM_ERROR("Invalid depth buffer offset\n");
                        return -EINVAL;
                }
+               dev_priv->have_z_offset = 1;
                break;
 
        case RADEON_EMIT_PP_CNTL:
-               if (radeon_check_and_fixup_offset(dev_priv, file_priv,
-                   &data[(RADEON_RB3D_COLOROFFSET - RADEON_PP_CNTL) / 4])) {
+               data = drm_buffer_pointer_to_dword(buf,
+                       (RADEON_RB3D_COLOROFFSET - RADEON_PP_CNTL) / 4);
+
+               if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) {
                        DRM_ERROR("Invalid colour buffer offset\n");
                        return -EINVAL;
                }
@@ -117,8 +124,8 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
        case R200_EMIT_PP_TXOFFSET_3:
        case R200_EMIT_PP_TXOFFSET_4:
        case R200_EMIT_PP_TXOFFSET_5:
-               if (radeon_check_and_fixup_offset(dev_priv, file_priv,
-                                                 &data[0])) {
+               data = drm_buffer_pointer_to_dword(buf, 0);
+               if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) {
                        DRM_ERROR("Invalid R200 texture offset\n");
                        return -EINVAL;
                }
@@ -127,8 +134,9 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
        case RADEON_EMIT_PP_TXFILTER_0:
        case RADEON_EMIT_PP_TXFILTER_1:
        case RADEON_EMIT_PP_TXFILTER_2:
-               if (radeon_check_and_fixup_offset(dev_priv, file_priv,
-                   &data[(RADEON_PP_TXOFFSET_0 - RADEON_PP_TXFILTER_0) / 4])) {
+               data = drm_buffer_pointer_to_dword(buf,
+                       (RADEON_PP_TXOFFSET_0 - RADEON_PP_TXFILTER_0) / 4);
+               if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) {
                        DRM_ERROR("Invalid R100 texture offset\n");
                        return -EINVAL;
                }
@@ -142,9 +150,10 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
        case R200_EMIT_PP_CUBIC_OFFSETS_5:{
                        int i;
                        for (i = 0; i < 5; i++) {
+                               data = drm_buffer_pointer_to_dword(buf, i);
                                if (radeon_check_and_fixup_offset(dev_priv,
                                                                  file_priv,
-                                                                 &data[i])) {
+                                                                 data)) {
                                        DRM_ERROR
                                            ("Invalid R200 cubic texture offset\n");
                                        return -EINVAL;
@@ -158,9 +167,10 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
        case RADEON_EMIT_PP_CUBIC_OFFSETS_T2:{
                        int i;
                        for (i = 0; i < 5; i++) {
+                               data = drm_buffer_pointer_to_dword(buf, i);
                                if (radeon_check_and_fixup_offset(dev_priv,
                                                                  file_priv,
-                                                                 &data[i])) {
+                                                                 data)) {
                                        DRM_ERROR
                                            ("Invalid R100 cubic texture offset\n");
                                        return -EINVAL;
@@ -269,23 +279,24 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
                                                     cmdbuf,
                                                     unsigned int *cmdsz)
 {
-       u32 *cmd = (u32 *) cmdbuf->buf;
+       u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0);
        u32 offset, narrays;
        int count, i, k;
 
-       *cmdsz = 2 + ((cmd[0] & RADEON_CP_PACKET_COUNT_MASK) >> 16);
+       count = ((*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16);
+       *cmdsz = 2 + count;
 
-       if ((cmd[0] & 0xc0000000) != RADEON_CP_PACKET3) {
+       if ((*cmd & 0xc0000000) != RADEON_CP_PACKET3) {
                DRM_ERROR("Not a type 3 packet\n");
                return -EINVAL;
        }
 
-       if (4 * *cmdsz > cmdbuf->bufsz) {
+       if (4 * *cmdsz > drm_buffer_unprocessed(cmdbuf->buffer)) {
                DRM_ERROR("Packet size larger than size of data provided\n");
                return -EINVAL;
        }
 
-       switch(cmd[0] & 0xff00) {
+       switch (*cmd & 0xff00) {
        /* XXX Are there old drivers needing other packets? */
 
        case RADEON_3D_DRAW_IMMD:
@@ -312,7 +323,6 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
                break;
 
        case RADEON_3D_LOAD_VBPNTR:
-               count = (cmd[0] >> 16) & 0x3fff;
 
                if (count > 18) { /* 12 arrays max */
                        DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n",
@@ -321,13 +331,16 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
                }
 
                /* carefully check packet contents */
-               narrays = cmd[1] & ~0xc000;
+               cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
+
+               narrays = *cmd & ~0xc000;
                k = 0;
                i = 2;
                while ((k < narrays) && (i < (count + 2))) {
                        i++;            /* skip attribute field */
+                       cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, i);
                        if (radeon_check_and_fixup_offset(dev_priv, file_priv,
-                                                         &cmd[i])) {
+                                                         cmd)) {
                                DRM_ERROR
                                    ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n",
                                     k, i);
@@ -338,8 +351,10 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
                        if (k == narrays)
                                break;
                        /* have one more to process, they come in pairs */
+                       cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, i);
+
                        if (radeon_check_and_fixup_offset(dev_priv,
-                                                         file_priv, &cmd[i]))
+                                                         file_priv, cmd))
                        {
                                DRM_ERROR
                                    ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n",
@@ -363,7 +378,9 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
                        DRM_ERROR("Invalid 3d packet for r200-class chip\n");
                        return -EINVAL;
                }
-               if (radeon_check_and_fixup_offset(dev_priv, file_priv, &cmd[1])) {
+
+               cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
+               if (radeon_check_and_fixup_offset(dev_priv, file_priv, cmd)) {
                                DRM_ERROR("Invalid rndr_gen_indx offset\n");
                                return -EINVAL;
                }
@@ -374,12 +391,15 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
                        DRM_ERROR("Invalid 3d packet for r100-class chip\n");
                        return -EINVAL;
                }
-               if ((cmd[1] & 0x8000ffff) != 0x80000810) {
-                       DRM_ERROR("Invalid indx_buffer reg address %08X\n", cmd[1]);
+
+               cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
+               if ((*cmd & 0x8000ffff) != 0x80000810) {
+                       DRM_ERROR("Invalid indx_buffer reg address %08X\n", *cmd);
                        return -EINVAL;
                }
-               if (radeon_check_and_fixup_offset(dev_priv, file_priv, &cmd[2])) {
-                       DRM_ERROR("Invalid indx_buffer offset is %08X\n", cmd[2]);
+               cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2);
+               if (radeon_check_and_fixup_offset(dev_priv, file_priv, cmd)) {
+                       DRM_ERROR("Invalid indx_buffer offset is %08X\n", *cmd);
                        return -EINVAL;
                }
                break;
@@ -388,31 +408,34 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
        case RADEON_CNTL_PAINT_MULTI:
        case RADEON_CNTL_BITBLT_MULTI:
                /* MSB of opcode: next DWORD GUI_CNTL */
-               if (cmd[1] & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL
+               cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1);
+               if (*cmd & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL
                              | RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
-                       offset = cmd[2] << 10;
+                       u32 *cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2);
+                       offset = *cmd2 << 10;
                        if (radeon_check_and_fixup_offset
                            (dev_priv, file_priv, &offset)) {
                                DRM_ERROR("Invalid first packet offset\n");
                                return -EINVAL;
                        }
-                       cmd[2] = (cmd[2] & 0xffc00000) | offset >> 10;
+                       *cmd2 = (*cmd2 & 0xffc00000) | offset >> 10;
                }
 
-               if ((cmd[1] & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) &&
-                   (cmd[1] & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
-                       offset = cmd[3] << 10;
+               if ((*cmd & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) &&
+                   (*cmd & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
+                       u32 *cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3);
+                       offset = *cmd << 10;
                        if (radeon_check_and_fixup_offset
                            (dev_priv, file_priv, &offset)) {
                                DRM_ERROR("Invalid second packet offset\n");
                                return -EINVAL;
                        }
-                       cmd[3] = (cmd[3] & 0xffc00000) | offset >> 10;
+                       *cmd3 = (*cmd3 & 0xffc00000) | offset >> 10;
                }
                break;
 
        default:
-               DRM_ERROR("Invalid packet type %x\n", cmd[0] & 0xff00);
+               DRM_ERROR("Invalid packet type %x\n", *cmd & 0xff00);
                return -EINVAL;
        }
 
@@ -876,6 +899,11 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
                if (tmp & RADEON_BACK)
                        flags |= RADEON_FRONT;
        }
+       if (flags & (RADEON_DEPTH|RADEON_STENCIL)) {
+               if (!dev_priv->have_z_offset)
+                       printk_once(KERN_ERR "radeon: illegal depth clear request. Buggy mesa detected - please update.\n");
+               flags &= ~(RADEON_DEPTH | RADEON_STENCIL);
+       }
 
        if (flags & (RADEON_FRONT | RADEON_BACK)) {
 
@@ -2611,7 +2639,6 @@ static int radeon_emit_packets(drm_radeon_private_t * dev_priv,
 {
        int id = (int)header.packet.packet_id;
        int sz, reg;
-       int *data = (int *)cmdbuf->buf;
        RING_LOCALS;
 
        if (id >= RADEON_MAX_STATE_PACKETS)
@@ -2620,23 +2647,22 @@ static int radeon_emit_packets(drm_radeon_private_t * dev_priv,
        sz = packet[id].len;
        reg = packet[id].start;
 
-       if (sz * sizeof(int) > cmdbuf->bufsz) {
+       if (sz * sizeof(u32) > drm_buffer_unprocessed(cmdbuf->buffer)) {
                DRM_ERROR("Packet size provided larger than data provided\n");
                return -EINVAL;
        }
 
-       if (radeon_check_and_fixup_packets(dev_priv, file_priv, id, data)) {
+       if (radeon_check_and_fixup_packets(dev_priv, file_priv, id,
+                               cmdbuf->buffer)) {
                DRM_ERROR("Packet verification failed\n");
                return -EINVAL;
        }
 
        BEGIN_RING(sz + 1);
        OUT_RING(CP_PACKET0(reg, (sz - 1)));
-       OUT_RING_TABLE(data, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * sizeof(int);
-       cmdbuf->bufsz -= sz * sizeof(int);
        return 0;
 }
 
@@ -2653,10 +2679,8 @@ static __inline__ int radeon_emit_scalars(drm_radeon_private_t *dev_priv,
        OUT_RING(CP_PACKET0(RADEON_SE_TCL_SCALAR_INDX_REG, 0));
        OUT_RING(start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT));
        OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_SCALAR_DATA_REG, sz - 1));
-       OUT_RING_TABLE(cmdbuf->buf, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
-       cmdbuf->buf += sz * sizeof(int);
-       cmdbuf->bufsz -= sz * sizeof(int);
        return 0;
 }
 
@@ -2675,10 +2699,8 @@ static __inline__ int radeon_emit_scalars2(drm_radeon_private_t *dev_priv,
        OUT_RING(CP_PACKET0(RADEON_SE_TCL_SCALAR_INDX_REG, 0));
        OUT_RING(start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT));
        OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_SCALAR_DATA_REG, sz - 1));
-       OUT_RING_TABLE(cmdbuf->buf, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
-       cmdbuf->buf += sz * sizeof(int);
-       cmdbuf->bufsz -= sz * sizeof(int);
        return 0;
 }
 
@@ -2696,11 +2718,9 @@ static __inline__ int radeon_emit_vectors(drm_radeon_private_t *dev_priv,
        OUT_RING(CP_PACKET0(RADEON_SE_TCL_VECTOR_INDX_REG, 0));
        OUT_RING(start | (stride << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT));
        OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_VECTOR_DATA_REG, (sz - 1)));
-       OUT_RING_TABLE(cmdbuf->buf, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * sizeof(int);
-       cmdbuf->bufsz -= sz * sizeof(int);
        return 0;
 }
 
@@ -2714,7 +2734,7 @@ static __inline__ int radeon_emit_veclinear(drm_radeon_private_t *dev_priv,
 
         if (!sz)
                 return 0;
-        if (sz * 4 > cmdbuf->bufsz)
+       if (sz * 4 > drm_buffer_unprocessed(cmdbuf->buffer))
                 return -EINVAL;
 
        BEGIN_RING(5 + sz);
@@ -2722,11 +2742,9 @@ static __inline__ int radeon_emit_veclinear(drm_radeon_private_t *dev_priv,
        OUT_RING(CP_PACKET0(RADEON_SE_TCL_VECTOR_INDX_REG, 0));
        OUT_RING(start | (1 << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT));
        OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_VECTOR_DATA_REG, (sz - 1)));
-       OUT_RING_TABLE(cmdbuf->buf, sz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz);
        ADVANCE_RING();
 
-       cmdbuf->buf += sz * sizeof(int);
-       cmdbuf->bufsz -= sz * sizeof(int);
        return 0;
 }
 
@@ -2748,11 +2766,9 @@ static int radeon_emit_packet3(struct drm_device * dev,
        }
 
        BEGIN_RING(cmdsz);
-       OUT_RING_TABLE(cmdbuf->buf, cmdsz);
+       OUT_RING_DRM_BUFFER(cmdbuf->buffer, cmdsz);
        ADVANCE_RING();
 
-       cmdbuf->buf += cmdsz * 4;
-       cmdbuf->bufsz -= cmdsz * 4;
        return 0;
 }
 
@@ -2805,16 +2821,16 @@ static int radeon_emit_packet3_cliprect(struct drm_device *dev,
                }
 
                BEGIN_RING(cmdsz);
-               OUT_RING_TABLE(cmdbuf->buf, cmdsz);
+               OUT_RING_DRM_BUFFER(cmdbuf->buffer, cmdsz);
                ADVANCE_RING();
 
        } while (++i < cmdbuf->nbox);
        if (cmdbuf->nbox == 1)
                cmdbuf->nbox = 0;
 
+       return 0;
       out:
-       cmdbuf->buf += cmdsz * 4;
-       cmdbuf->bufsz -= cmdsz * 4;
+       drm_buffer_advance(cmdbuf->buffer, cmdsz * 4);
        return 0;
 }
 
@@ -2847,16 +2863,16 @@ static int radeon_emit_wait(struct drm_device * dev, int flags)
        return 0;
 }
 
-static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_priv)
+static int radeon_cp_cmdbuf(struct drm_device *dev, void *data,
+               struct drm_file *file_priv)
 {
        drm_radeon_private_t *dev_priv = dev->dev_private;
        struct drm_device_dma *dma = dev->dma;
        struct drm_buf *buf = NULL;
+       drm_radeon_cmd_header_t stack_header;
        int idx;
        drm_radeon_kcmd_buffer_t *cmdbuf = data;
-       drm_radeon_cmd_header_t header;
-       int orig_nbox, orig_bufsz;
-       char *kbuf = NULL;
+       int orig_nbox;
 
        LOCK_TEST_WITH_RETURN(dev, file_priv);
 
@@ -2871,17 +2887,16 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
         * races between checking values and using those values in other code,
         * and simply to avoid a lot of function calls to copy in data.
         */
-       orig_bufsz = cmdbuf->bufsz;
-       if (orig_bufsz != 0) {
-               kbuf = kmalloc(cmdbuf->bufsz, GFP_KERNEL);
-               if (kbuf == NULL)
-                       return -ENOMEM;
-               if (DRM_COPY_FROM_USER(kbuf, (void __user *)cmdbuf->buf,
-                                      cmdbuf->bufsz)) {
-                       kfree(kbuf);
-                       return -EFAULT;
-               }
-               cmdbuf->buf = kbuf;
+       if (cmdbuf->bufsz != 0) {
+               int rv;
+               void __user *buffer = cmdbuf->buffer;
+               rv = drm_buffer_alloc(&cmdbuf->buffer, cmdbuf->bufsz);
+               if (rv)
+                       return rv;
+               rv = drm_buffer_copy_from_user(cmdbuf->buffer, buffer,
+                                               cmdbuf->bufsz);
+               if (rv)
+                       return rv;
        }
 
        orig_nbox = cmdbuf->nbox;
@@ -2890,24 +2905,24 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
                int temp;
                temp = r300_do_cp_cmdbuf(dev, file_priv, cmdbuf);
 
-               if (orig_bufsz != 0)
-                       kfree(kbuf);
+               if (cmdbuf->bufsz != 0)
+                       drm_buffer_free(cmdbuf->buffer);
 
                return temp;
        }
 
        /* microcode_version != r300 */
-       while (cmdbuf->bufsz >= sizeof(header)) {
+       while (drm_buffer_unprocessed(cmdbuf->buffer) >= sizeof(stack_header)) {
 
-               header.i = *(int *)cmdbuf->buf;
-               cmdbuf->buf += sizeof(header);
-               cmdbuf->bufsz -= sizeof(header);
+               drm_radeon_cmd_header_t *header;
+               header = drm_buffer_read_object(cmdbuf->buffer,
+                               sizeof(stack_header), &stack_header);
 
-               switch (header.header.cmd_type) {
+               switch (header->header.cmd_type) {
                case RADEON_CMD_PACKET:
                        DRM_DEBUG("RADEON_CMD_PACKET\n");
                        if (radeon_emit_packets
-                           (dev_priv, file_priv, header, cmdbuf)) {
+                           (dev_priv, file_priv, *header, cmdbuf)) {
                                DRM_ERROR("radeon_emit_packets failed\n");
                                goto err;
                        }
@@ -2915,7 +2930,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
 
                case RADEON_CMD_SCALARS:
                        DRM_DEBUG("RADEON_CMD_SCALARS\n");
-                       if (radeon_emit_scalars(dev_priv, header, cmdbuf)) {
+                       if (radeon_emit_scalars(dev_priv, *header, cmdbuf)) {
                                DRM_ERROR("radeon_emit_scalars failed\n");
                                goto err;
                        }
@@ -2923,7 +2938,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
 
                case RADEON_CMD_VECTORS:
                        DRM_DEBUG("RADEON_CMD_VECTORS\n");
-                       if (radeon_emit_vectors(dev_priv, header, cmdbuf)) {
+                       if (radeon_emit_vectors(dev_priv, *header, cmdbuf)) {
                                DRM_ERROR("radeon_emit_vectors failed\n");
                                goto err;
                        }
@@ -2931,7 +2946,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
 
                case RADEON_CMD_DMA_DISCARD:
                        DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n");
-                       idx = header.dma.buf_idx;
+                       idx = header->dma.buf_idx;
                        if (idx < 0 || idx >= dma->buf_count) {
                                DRM_ERROR("buffer index %d (of %d max)\n",
                                          idx, dma->buf_count - 1);
@@ -2968,7 +2983,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
 
                case RADEON_CMD_SCALARS2:
                        DRM_DEBUG("RADEON_CMD_SCALARS2\n");
-                       if (radeon_emit_scalars2(dev_priv, header, cmdbuf)) {
+                       if (radeon_emit_scalars2(dev_priv, *header, cmdbuf)) {
                                DRM_ERROR("radeon_emit_scalars2 failed\n");
                                goto err;
                        }
@@ -2976,37 +2991,37 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
 
                case RADEON_CMD_WAIT:
                        DRM_DEBUG("RADEON_CMD_WAIT\n");
-                       if (radeon_emit_wait(dev, header.wait.flags)) {
+                       if (radeon_emit_wait(dev, header->wait.flags)) {
                                DRM_ERROR("radeon_emit_wait failed\n");
                                goto err;
                        }
                        break;
                case RADEON_CMD_VECLINEAR:
                        DRM_DEBUG("RADEON_CMD_VECLINEAR\n");
-                       if (radeon_emit_veclinear(dev_priv, header, cmdbuf)) {
+                       if (radeon_emit_veclinear(dev_priv, *header, cmdbuf)) {
                                DRM_ERROR("radeon_emit_veclinear failed\n");
                                goto err;
                        }
                        break;
 
                default:
-                       DRM_ERROR("bad cmd_type %d at %p\n",
-                                 header.header.cmd_type,
-                                 cmdbuf->buf - sizeof(header));
+                       DRM_ERROR("bad cmd_type %d at byte %d\n",
+                                 header->header.cmd_type,
+                                 cmdbuf->buffer->iterator);
                        goto err;
                }
        }
 
-       if (orig_bufsz != 0)
-               kfree(kbuf);
+       if (cmdbuf->bufsz != 0)
+               drm_buffer_free(cmdbuf->buffer);
 
        DRM_DEBUG("DONE\n");
        COMMIT_RING();
        return 0;
 
       err:
-       if (orig_bufsz != 0)
-               kfree(kbuf);
+       if (cmdbuf->bufsz != 0)
+               drm_buffer_free(cmdbuf->buffer);
        return -EINVAL;
 }
 
index 9f5e2f929da98e92cd8c418d010587ad7045d451..313c96bc09da1dcfcb0703ed3aa82a2038af0731 100644 (file)
@@ -186,7 +186,7 @@ void radeon_test_moves(struct radeon_device *rdev)
                radeon_bo_kunmap(gtt_obj[i]);
 
                DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n",
-                        gtt_addr - rdev->mc.gtt_location);
+                        gtt_addr - rdev->mc.gtt_start);
        }
 
 out_cleanup:
index 58b5adf974ca8bac6434025e3962443af28c0d3b..43c5ab34b634bf78383b940e3e2bafdda4c5d89e 100644 (file)
@@ -150,7 +150,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                man->default_caching = TTM_PL_FLAG_CACHED;
                break;
        case TTM_PL_TT:
-               man->gpu_offset = rdev->mc.gtt_location;
+               man->gpu_offset = rdev->mc.gtt_start;
                man->available_caching = TTM_PL_MASK_CACHING;
                man->default_caching = TTM_PL_FLAG_CACHED;
                man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA;
@@ -180,7 +180,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                break;
        case TTM_PL_VRAM:
                /* "On-card" video ram */
-               man->gpu_offset = rdev->mc.vram_location;
+               man->gpu_offset = rdev->mc.vram_start;
                man->flags = TTM_MEMTYPE_FLAG_FIXED |
                             TTM_MEMTYPE_FLAG_NEEDS_IOREMAP |
                             TTM_MEMTYPE_FLAG_MAPPABLE;
@@ -262,10 +262,10 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
 
        switch (old_mem->mem_type) {
        case TTM_PL_VRAM:
-               old_start += rdev->mc.vram_location;
+               old_start += rdev->mc.vram_start;
                break;
        case TTM_PL_TT:
-               old_start += rdev->mc.gtt_location;
+               old_start += rdev->mc.gtt_start;
                break;
        default:
                DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
@@ -273,10 +273,10 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
        }
        switch (new_mem->mem_type) {
        case TTM_PL_VRAM:
-               new_start += rdev->mc.vram_location;
+               new_start += rdev->mc.vram_start;
                break;
        case TTM_PL_TT:
-               new_start += rdev->mc.gtt_location;
+               new_start += rdev->mc.gtt_start;
                break;
        default:
                DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
new file mode 100644 (file)
index 0000000..8f414a5
--- /dev/null
@@ -0,0 +1,837 @@
+r600 0x9400
+0x000287A0 R7xx_CB_SHADER_CONTROL
+0x00028230 R7xx_PA_SC_EDGERULE
+0x000286C8 R7xx_SPI_THREAD_GROUPING
+0x00008D8C R7xx_SQ_DYN_GPR_CNTL_PS_FLUSH_REQ
+0x000088C4 VGT_CACHE_INVALIDATION
+0x00028A50 VGT_ENHANCE
+0x000088CC VGT_ES_PER_GS
+0x00028A2C VGT_GROUP_DECR
+0x00028A28 VGT_GROUP_FIRST_DECR
+0x00028A24 VGT_GROUP_PRIM_TYPE
+0x00028A30 VGT_GROUP_VECT_0_CNTL
+0x00028A38 VGT_GROUP_VECT_0_FMT_CNTL
+0x00028A34 VGT_GROUP_VECT_1_CNTL
+0x00028A3C VGT_GROUP_VECT_1_FMT_CNTL
+0x00028A40 VGT_GS_MODE
+0x00028A6C VGT_GS_OUT_PRIM_TYPE
+0x000088C8 VGT_GS_PER_ES
+0x000088E8 VGT_GS_PER_VS
+0x000088D4 VGT_GS_VERTEX_REUSE
+0x00028A14 VGT_HOS_CNTL
+0x00028A18 VGT_HOS_MAX_TESS_LEVEL
+0x00028A1C VGT_HOS_MIN_TESS_LEVEL
+0x00028A20 VGT_HOS_REUSE_DEPTH
+0x0000895C VGT_INDEX_TYPE
+0x00028408 VGT_INDX_OFFSET
+0x00028AA0 VGT_INSTANCE_STEP_RATE_0
+0x00028AA4 VGT_INSTANCE_STEP_RATE_1
+0x000088C0 VGT_LAST_COPY_STATE
+0x00028400 VGT_MAX_VTX_INDX
+0x000088D8 VGT_MC_LAT_CNTL
+0x00028404 VGT_MIN_VTX_INDX
+0x00028A94 VGT_MULTI_PRIM_IB_RESET_EN
+0x0002840C VGT_MULTI_PRIM_IB_RESET_INDX
+0x00008970 VGT_NUM_INDICES
+0x00008974 VGT_NUM_INSTANCES
+0x00028A10 VGT_OUTPUT_PATH_CNTL
+0x00028C5C VGT_OUT_DEALLOC_CNTL
+0x00028A84 VGT_PRIMITIVEID_EN
+0x00008958 VGT_PRIMITIVE_TYPE
+0x00028AB4 VGT_REUSE_OFF
+0x00028C58 VGT_VERTEX_REUSE_BLOCK_CNTL
+0x00028AB8 VGT_VTX_CNT_EN
+0x000088B0 VGT_VTX_VECT_EJECT_REG
+0x00028810 PA_CL_CLIP_CNTL
+0x00008A14 PA_CL_ENHANCE
+0x00028C14 PA_CL_GB_HORZ_CLIP_ADJ
+0x00028C18 PA_CL_GB_HORZ_DISC_ADJ
+0x00028C0C PA_CL_GB_VERT_CLIP_ADJ
+0x00028C10 PA_CL_GB_VERT_DISC_ADJ
+0x00028820 PA_CL_NANINF_CNTL
+0x00028E1C PA_CL_POINT_CULL_RAD
+0x00028E18 PA_CL_POINT_SIZE
+0x00028E10 PA_CL_POINT_X_RAD
+0x00028E14 PA_CL_POINT_Y_RAD
+0x00028E2C PA_CL_UCP_0_W
+0x00028E3C PA_CL_UCP_1_W
+0x00028E4C PA_CL_UCP_2_W
+0x00028E5C PA_CL_UCP_3_W
+0x00028E6C PA_CL_UCP_4_W
+0x00028E7C PA_CL_UCP_5_W
+0x00028E20 PA_CL_UCP_0_X
+0x00028E30 PA_CL_UCP_1_X
+0x00028E40 PA_CL_UCP_2_X
+0x00028E50 PA_CL_UCP_3_X
+0x00028E60 PA_CL_UCP_4_X
+0x00028E70 PA_CL_UCP_5_X
+0x00028E24 PA_CL_UCP_0_Y
+0x00028E34 PA_CL_UCP_1_Y
+0x00028E44 PA_CL_UCP_2_Y
+0x00028E54 PA_CL_UCP_3_Y
+0x00028E64 PA_CL_UCP_4_Y
+0x00028E74 PA_CL_UCP_5_Y
+0x00028E28 PA_CL_UCP_0_Z
+0x00028E38 PA_CL_UCP_1_Z
+0x00028E48 PA_CL_UCP_2_Z
+0x00028E58 PA_CL_UCP_3_Z
+0x00028E68 PA_CL_UCP_4_Z
+0x00028E78 PA_CL_UCP_5_Z
+0x00028440 PA_CL_VPORT_XOFFSET_0
+0x00028458 PA_CL_VPORT_XOFFSET_1
+0x00028470 PA_CL_VPORT_XOFFSET_2
+0x00028488 PA_CL_VPORT_XOFFSET_3
+0x000284A0 PA_CL_VPORT_XOFFSET_4
+0x000284B8 PA_CL_VPORT_XOFFSET_5
+0x000284D0 PA_CL_VPORT_XOFFSET_6
+0x000284E8 PA_CL_VPORT_XOFFSET_7
+0x00028500 PA_CL_VPORT_XOFFSET_8
+0x00028518 PA_CL_VPORT_XOFFSET_9
+0x00028530 PA_CL_VPORT_XOFFSET_10
+0x00028548 PA_CL_VPORT_XOFFSET_11
+0x00028560 PA_CL_VPORT_XOFFSET_12
+0x00028578 PA_CL_VPORT_XOFFSET_13
+0x00028590 PA_CL_VPORT_XOFFSET_14
+0x000285A8 PA_CL_VPORT_XOFFSET_15
+0x0002843C PA_CL_VPORT_XSCALE_0
+0x00028454 PA_CL_VPORT_XSCALE_1
+0x0002846C PA_CL_VPORT_XSCALE_2
+0x00028484 PA_CL_VPORT_XSCALE_3
+0x0002849C PA_CL_VPORT_XSCALE_4
+0x000284B4 PA_CL_VPORT_XSCALE_5
+0x000284CC PA_CL_VPORT_XSCALE_6
+0x000284E4 PA_CL_VPORT_XSCALE_7
+0x000284FC PA_CL_VPORT_XSCALE_8
+0x00028514 PA_CL_VPORT_XSCALE_9
+0x0002852C PA_CL_VPORT_XSCALE_10
+0x00028544 PA_CL_VPORT_XSCALE_11
+0x0002855C PA_CL_VPORT_XSCALE_12
+0x00028574 PA_CL_VPORT_XSCALE_13
+0x0002858C PA_CL_VPORT_XSCALE_14
+0x000285A4 PA_CL_VPORT_XSCALE_15
+0x00028448 PA_CL_VPORT_YOFFSET_0
+0x00028460 PA_CL_VPORT_YOFFSET_1
+0x00028478 PA_CL_VPORT_YOFFSET_2
+0x00028490 PA_CL_VPORT_YOFFSET_3
+0x000284A8 PA_CL_VPORT_YOFFSET_4
+0x000284C0 PA_CL_VPORT_YOFFSET_5
+0x000284D8 PA_CL_VPORT_YOFFSET_6
+0x000284F0 PA_CL_VPORT_YOFFSET_7
+0x00028508 PA_CL_VPORT_YOFFSET_8
+0x00028520 PA_CL_VPORT_YOFFSET_9
+0x00028538 PA_CL_VPORT_YOFFSET_10
+0x00028550 PA_CL_VPORT_YOFFSET_11
+0x00028568 PA_CL_VPORT_YOFFSET_12
+0x00028580 PA_CL_VPORT_YOFFSET_13
+0x00028598 PA_CL_VPORT_YOFFSET_14
+0x000285B0 PA_CL_VPORT_YOFFSET_15
+0x00028444 PA_CL_VPORT_YSCALE_0
+0x0002845C PA_CL_VPORT_YSCALE_1
+0x00028474 PA_CL_VPORT_YSCALE_2
+0x0002848C PA_CL_VPORT_YSCALE_3
+0x000284A4 PA_CL_VPORT_YSCALE_4
+0x000284BC PA_CL_VPORT_YSCALE_5
+0x000284D4 PA_CL_VPORT_YSCALE_6
+0x000284EC PA_CL_VPORT_YSCALE_7
+0x00028504 PA_CL_VPORT_YSCALE_8
+0x0002851C PA_CL_VPORT_YSCALE_9
+0x00028534 PA_CL_VPORT_YSCALE_10
+0x0002854C PA_CL_VPORT_YSCALE_11
+0x00028564 PA_CL_VPORT_YSCALE_12
+0x0002857C PA_CL_VPORT_YSCALE_13
+0x00028594 PA_CL_VPORT_YSCALE_14
+0x000285AC PA_CL_VPORT_YSCALE_15
+0x00028450 PA_CL_VPORT_ZOFFSET_0
+0x00028468 PA_CL_VPORT_ZOFFSET_1
+0x00028480 PA_CL_VPORT_ZOFFSET_2
+0x00028498 PA_CL_VPORT_ZOFFSET_3
+0x000284B0 PA_CL_VPORT_ZOFFSET_4
+0x000284C8 PA_CL_VPORT_ZOFFSET_5
+0x000284E0 PA_CL_VPORT_ZOFFSET_6
+0x000284F8 PA_CL_VPORT_ZOFFSET_7
+0x00028510 PA_CL_VPORT_ZOFFSET_8
+0x00028528 PA_CL_VPORT_ZOFFSET_9
+0x00028540 PA_CL_VPORT_ZOFFSET_10
+0x00028558 PA_CL_VPORT_ZOFFSET_11
+0x00028570 PA_CL_VPORT_ZOFFSET_12
+0x00028588 PA_CL_VPORT_ZOFFSET_13
+0x000285A0 PA_CL_VPORT_ZOFFSET_14
+0x000285B8 PA_CL_VPORT_ZOFFSET_15
+0x0002844C PA_CL_VPORT_ZSCALE_0
+0x00028464 PA_CL_VPORT_ZSCALE_1
+0x0002847C PA_CL_VPORT_ZSCALE_2
+0x00028494 PA_CL_VPORT_ZSCALE_3
+0x000284AC PA_CL_VPORT_ZSCALE_4
+0x000284C4 PA_CL_VPORT_ZSCALE_5
+0x000284DC PA_CL_VPORT_ZSCALE_6
+0x000284F4 PA_CL_VPORT_ZSCALE_7
+0x0002850C PA_CL_VPORT_ZSCALE_8
+0x00028524 PA_CL_VPORT_ZSCALE_9
+0x0002853C PA_CL_VPORT_ZSCALE_10
+0x00028554 PA_CL_VPORT_ZSCALE_11
+0x0002856C PA_CL_VPORT_ZSCALE_12
+0x00028584 PA_CL_VPORT_ZSCALE_13
+0x0002859C PA_CL_VPORT_ZSCALE_14
+0x000285B4 PA_CL_VPORT_ZSCALE_15
+0x0002881C PA_CL_VS_OUT_CNTL
+0x00028818 PA_CL_VTE_CNTL
+0x00028C48 PA_SC_AA_MASK
+0x00008B40 PA_SC_AA_SAMPLE_LOCS_2S
+0x00008B44 PA_SC_AA_SAMPLE_LOCS_4S
+0x00008B48 PA_SC_AA_SAMPLE_LOCS_8S_WD0
+0x00008B4C PA_SC_AA_SAMPLE_LOCS_8S_WD1
+0x00028C20 PA_SC_AA_SAMPLE_LOCS_8S_WD1_MCTX
+0x00028C1C PA_SC_AA_SAMPLE_LOCS_MCTX
+0x00028214 PA_SC_CLIPRECT_0_BR
+0x0002821C PA_SC_CLIPRECT_1_BR
+0x00028224 PA_SC_CLIPRECT_2_BR
+0x0002822C PA_SC_CLIPRECT_3_BR
+0x00028210 PA_SC_CLIPRECT_0_TL
+0x00028218 PA_SC_CLIPRECT_1_TL
+0x00028220 PA_SC_CLIPRECT_2_TL
+0x00028228 PA_SC_CLIPRECT_3_TL
+0x0002820C PA_SC_CLIPRECT_RULE
+0x00008BF0 PA_SC_ENHANCE
+0x00028244 PA_SC_GENERIC_SCISSOR_BR
+0x00028240 PA_SC_GENERIC_SCISSOR_TL
+0x00028C00 PA_SC_LINE_CNTL
+0x00028A0C PA_SC_LINE_STIPPLE
+0x00008B10 PA_SC_LINE_STIPPLE_STATE
+0x00028A4C PA_SC_MODE_CNTL
+0x00028A48 PA_SC_MPASS_PS_CNTL
+0x00008B20 PA_SC_MULTI_CHIP_CNTL
+0x00028034 PA_SC_SCREEN_SCISSOR_BR
+0x00028030 PA_SC_SCREEN_SCISSOR_TL
+0x00028254 PA_SC_VPORT_SCISSOR_0_BR
+0x0002825C PA_SC_VPORT_SCISSOR_1_BR
+0x00028264 PA_SC_VPORT_SCISSOR_2_BR
+0x0002826C PA_SC_VPORT_SCISSOR_3_BR
+0x00028274 PA_SC_VPORT_SCISSOR_4_BR
+0x0002827C PA_SC_VPORT_SCISSOR_5_BR
+0x00028284 PA_SC_VPORT_SCISSOR_6_BR
+0x0002828C PA_SC_VPORT_SCISSOR_7_BR
+0x00028294 PA_SC_VPORT_SCISSOR_8_BR
+0x0002829C PA_SC_VPORT_SCISSOR_9_BR
+0x000282A4 PA_SC_VPORT_SCISSOR_10_BR
+0x000282AC PA_SC_VPORT_SCISSOR_11_BR
+0x000282B4 PA_SC_VPORT_SCISSOR_12_BR
+0x000282BC PA_SC_VPORT_SCISSOR_13_BR
+0x000282C4 PA_SC_VPORT_SCISSOR_14_BR
+0x000282CC PA_SC_VPORT_SCISSOR_15_BR
+0x00028250 PA_SC_VPORT_SCISSOR_0_TL
+0x00028258 PA_SC_VPORT_SCISSOR_1_TL
+0x00028260 PA_SC_VPORT_SCISSOR_2_TL
+0x00028268 PA_SC_VPORT_SCISSOR_3_TL
+0x00028270 PA_SC_VPORT_SCISSOR_4_TL
+0x00028278 PA_SC_VPORT_SCISSOR_5_TL
+0x00028280 PA_SC_VPORT_SCISSOR_6_TL
+0x00028288 PA_SC_VPORT_SCISSOR_7_TL
+0x00028290 PA_SC_VPORT_SCISSOR_8_TL
+0x00028298 PA_SC_VPORT_SCISSOR_9_TL
+0x000282A0 PA_SC_VPORT_SCISSOR_10_TL
+0x000282A8 PA_SC_VPORT_SCISSOR_11_TL
+0x000282B0 PA_SC_VPORT_SCISSOR_12_TL
+0x000282B8 PA_SC_VPORT_SCISSOR_13_TL
+0x000282C0 PA_SC_VPORT_SCISSOR_14_TL
+0x000282C8 PA_SC_VPORT_SCISSOR_15_TL
+0x000282D4 PA_SC_VPORT_ZMAX_0
+0x000282DC PA_SC_VPORT_ZMAX_1
+0x000282E4 PA_SC_VPORT_ZMAX_2
+0x000282EC PA_SC_VPORT_ZMAX_3
+0x000282F4 PA_SC_VPORT_ZMAX_4
+0x000282FC PA_SC_VPORT_ZMAX_5
+0x00028304 PA_SC_VPORT_ZMAX_6
+0x0002830C PA_SC_VPORT_ZMAX_7
+0x00028314 PA_SC_VPORT_ZMAX_8
+0x0002831C PA_SC_VPORT_ZMAX_9
+0x00028324 PA_SC_VPORT_ZMAX_10
+0x0002832C PA_SC_VPORT_ZMAX_11
+0x00028334 PA_SC_VPORT_ZMAX_12
+0x0002833C PA_SC_VPORT_ZMAX_13
+0x00028344 PA_SC_VPORT_ZMAX_14
+0x0002834C PA_SC_VPORT_ZMAX_15
+0x000282D0 PA_SC_VPORT_ZMIN_0
+0x000282D8 PA_SC_VPORT_ZMIN_1
+0x000282E0 PA_SC_VPORT_ZMIN_2
+0x000282E8 PA_SC_VPORT_ZMIN_3
+0x000282F0 PA_SC_VPORT_ZMIN_4
+0x000282F8 PA_SC_VPORT_ZMIN_5
+0x00028300 PA_SC_VPORT_ZMIN_6
+0x00028308 PA_SC_VPORT_ZMIN_7
+0x00028310 PA_SC_VPORT_ZMIN_8
+0x00028318 PA_SC_VPORT_ZMIN_9
+0x00028320 PA_SC_VPORT_ZMIN_10
+0x00028328 PA_SC_VPORT_ZMIN_11
+0x00028330 PA_SC_VPORT_ZMIN_12
+0x00028338 PA_SC_VPORT_ZMIN_13
+0x00028340 PA_SC_VPORT_ZMIN_14
+0x00028348 PA_SC_VPORT_ZMIN_15
+0x00028200 PA_SC_WINDOW_OFFSET
+0x00028208 PA_SC_WINDOW_SCISSOR_BR
+0x00028204 PA_SC_WINDOW_SCISSOR_TL
+0x00028A08 PA_SU_LINE_CNTL
+0x00028A04 PA_SU_POINT_MINMAX
+0x00028A00 PA_SU_POINT_SIZE
+0x00028E0C PA_SU_POLY_OFFSET_BACK_OFFSET
+0x00028E08 PA_SU_POLY_OFFSET_BACK_SCALE
+0x00028DFC PA_SU_POLY_OFFSET_CLAMP
+0x00028DF8 PA_SU_POLY_OFFSET_DB_FMT_CNTL
+0x00028E04 PA_SU_POLY_OFFSET_FRONT_OFFSET
+0x00028E00 PA_SU_POLY_OFFSET_FRONT_SCALE
+0x00028814 PA_SU_SC_MODE_CNTL
+0x00028C08 PA_SU_VTX_CNTL
+0x00008C00 SQ_CONFIG
+0x00008C04 SQ_GPR_RESOURCE_MGMT_1
+0x00008C08 SQ_GPR_RESOURCE_MGMT_2
+0x00008C10 SQ_STACK_RESOURCE_MGMT_1
+0x00008C14 SQ_STACK_RESOURCE_MGMT_2
+0x00008C0C SQ_THREAD_RESOURCE_MGMT
+0x00028380 SQ_VTX_SEMANTIC_0
+0x00028384 SQ_VTX_SEMANTIC_1
+0x00028388 SQ_VTX_SEMANTIC_2
+0x0002838C SQ_VTX_SEMANTIC_3
+0x00028390 SQ_VTX_SEMANTIC_4
+0x00028394 SQ_VTX_SEMANTIC_5
+0x00028398 SQ_VTX_SEMANTIC_6
+0x0002839C SQ_VTX_SEMANTIC_7
+0x000283A0 SQ_VTX_SEMANTIC_8
+0x000283A4 SQ_VTX_SEMANTIC_9
+0x000283A8 SQ_VTX_SEMANTIC_10
+0x000283AC SQ_VTX_SEMANTIC_11
+0x000283B0 SQ_VTX_SEMANTIC_12
+0x000283B4 SQ_VTX_SEMANTIC_13
+0x000283B8 SQ_VTX_SEMANTIC_14
+0x000283BC SQ_VTX_SEMANTIC_15
+0x000283C0 SQ_VTX_SEMANTIC_16
+0x000283C4 SQ_VTX_SEMANTIC_17
+0x000283C8 SQ_VTX_SEMANTIC_18
+0x000283CC SQ_VTX_SEMANTIC_19
+0x000283D0 SQ_VTX_SEMANTIC_20
+0x000283D4 SQ_VTX_SEMANTIC_21
+0x000283D8 SQ_VTX_SEMANTIC_22
+0x000283DC SQ_VTX_SEMANTIC_23
+0x000283E0 SQ_VTX_SEMANTIC_24
+0x000283E4 SQ_VTX_SEMANTIC_25
+0x000283E8 SQ_VTX_SEMANTIC_26
+0x000283EC SQ_VTX_SEMANTIC_27
+0x000283F0 SQ_VTX_SEMANTIC_28
+0x000283F4 SQ_VTX_SEMANTIC_29
+0x000283F8 SQ_VTX_SEMANTIC_30
+0x000283FC SQ_VTX_SEMANTIC_31
+0x000288E0 SQ_VTX_SEMANTIC_CLEAR
+0x0003CFF4 SQ_VTX_START_INST_LOC
+0x0003C000 SQ_TEX_SAMPLER_WORD0_0
+0x0003C004 SQ_TEX_SAMPLER_WORD1_0
+0x0003C008 SQ_TEX_SAMPLER_WORD2_0
+0x00030000 SQ_ALU_CONSTANT0_0
+0x00030004 SQ_ALU_CONSTANT1_0
+0x00030008 SQ_ALU_CONSTANT2_0
+0x0003000C SQ_ALU_CONSTANT3_0
+0x0003E380 SQ_BOOL_CONST_0
+0x0003E384 SQ_BOOL_CONST_1
+0x0003E388 SQ_BOOL_CONST_2
+0x0003E200 SQ_LOOP_CONST_0
+0x0003E200 SQ_LOOP_CONST_DX10_0
+0x000281C0 SQ_ALU_CONST_BUFFER_SIZE_GS_0
+0x000281C4 SQ_ALU_CONST_BUFFER_SIZE_GS_1
+0x000281C8 SQ_ALU_CONST_BUFFER_SIZE_GS_2
+0x000281CC SQ_ALU_CONST_BUFFER_SIZE_GS_3
+0x000281D0 SQ_ALU_CONST_BUFFER_SIZE_GS_4
+0x000281D4 SQ_ALU_CONST_BUFFER_SIZE_GS_5
+0x000281D8 SQ_ALU_CONST_BUFFER_SIZE_GS_6
+0x000281DC SQ_ALU_CONST_BUFFER_SIZE_GS_7
+0x000281E0 SQ_ALU_CONST_BUFFER_SIZE_GS_8
+0x000281E4 SQ_ALU_CONST_BUFFER_SIZE_GS_9
+0x000281E8 SQ_ALU_CONST_BUFFER_SIZE_GS_10
+0x000281EC SQ_ALU_CONST_BUFFER_SIZE_GS_11
+0x000281F0 SQ_ALU_CONST_BUFFER_SIZE_GS_12
+0x000281F4 SQ_ALU_CONST_BUFFER_SIZE_GS_13
+0x000281F8 SQ_ALU_CONST_BUFFER_SIZE_GS_14
+0x000281FC SQ_ALU_CONST_BUFFER_SIZE_GS_15
+0x00028140 SQ_ALU_CONST_BUFFER_SIZE_PS_0
+0x00028144 SQ_ALU_CONST_BUFFER_SIZE_PS_1
+0x00028148 SQ_ALU_CONST_BUFFER_SIZE_PS_2
+0x0002814C SQ_ALU_CONST_BUFFER_SIZE_PS_3
+0x00028150 SQ_ALU_CONST_BUFFER_SIZE_PS_4
+0x00028154 SQ_ALU_CONST_BUFFER_SIZE_PS_5
+0x00028158 SQ_ALU_CONST_BUFFER_SIZE_PS_6
+0x0002815C SQ_ALU_CONST_BUFFER_SIZE_PS_7
+0x00028160 SQ_ALU_CONST_BUFFER_SIZE_PS_8
+0x00028164 SQ_ALU_CONST_BUFFER_SIZE_PS_9
+0x00028168 SQ_ALU_CONST_BUFFER_SIZE_PS_10
+0x0002816C SQ_ALU_CONST_BUFFER_SIZE_PS_11
+0x00028170 SQ_ALU_CONST_BUFFER_SIZE_PS_12
+0x00028174 SQ_ALU_CONST_BUFFER_SIZE_PS_13
+0x00028178 SQ_ALU_CONST_BUFFER_SIZE_PS_14
+0x0002817C SQ_ALU_CONST_BUFFER_SIZE_PS_15
+0x00028180 SQ_ALU_CONST_BUFFER_SIZE_VS_0
+0x00028184 SQ_ALU_CONST_BUFFER_SIZE_VS_1
+0x00028188 SQ_ALU_CONST_BUFFER_SIZE_VS_2
+0x0002818C SQ_ALU_CONST_BUFFER_SIZE_VS_3
+0x00028190 SQ_ALU_CONST_BUFFER_SIZE_VS_4
+0x00028194 SQ_ALU_CONST_BUFFER_SIZE_VS_5
+0x00028198 SQ_ALU_CONST_BUFFER_SIZE_VS_6
+0x0002819C SQ_ALU_CONST_BUFFER_SIZE_VS_7
+0x000281A0 SQ_ALU_CONST_BUFFER_SIZE_VS_8
+0x000281A4 SQ_ALU_CONST_BUFFER_SIZE_VS_9
+0x000281A8 SQ_ALU_CONST_BUFFER_SIZE_VS_10
+0x000281AC SQ_ALU_CONST_BUFFER_SIZE_VS_11
+0x000281B0 SQ_ALU_CONST_BUFFER_SIZE_VS_12
+0x000281B4 SQ_ALU_CONST_BUFFER_SIZE_VS_13
+0x000281B8 SQ_ALU_CONST_BUFFER_SIZE_VS_14
+0x000281BC SQ_ALU_CONST_BUFFER_SIZE_VS_15
+0x000289C0 SQ_ALU_CONST_CACHE_GS_0
+0x000289C4 SQ_ALU_CONST_CACHE_GS_1
+0x000289C8 SQ_ALU_CONST_CACHE_GS_2
+0x000289CC SQ_ALU_CONST_CACHE_GS_3
+0x000289D0 SQ_ALU_CONST_CACHE_GS_4
+0x000289D4 SQ_ALU_CONST_CACHE_GS_5
+0x000289D8 SQ_ALU_CONST_CACHE_GS_6
+0x000289DC SQ_ALU_CONST_CACHE_GS_7
+0x000289E0 SQ_ALU_CONST_CACHE_GS_8
+0x000289E4 SQ_ALU_CONST_CACHE_GS_9
+0x000289E8 SQ_ALU_CONST_CACHE_GS_10
+0x000289EC SQ_ALU_CONST_CACHE_GS_11
+0x000289F0 SQ_ALU_CONST_CACHE_GS_12
+0x000289F4 SQ_ALU_CONST_CACHE_GS_13
+0x000289F8 SQ_ALU_CONST_CACHE_GS_14
+0x000289FC SQ_ALU_CONST_CACHE_GS_15
+0x00028940 SQ_ALU_CONST_CACHE_PS_0
+0x00028944 SQ_ALU_CONST_CACHE_PS_1
+0x00028948 SQ_ALU_CONST_CACHE_PS_2
+0x0002894C SQ_ALU_CONST_CACHE_PS_3
+0x00028950 SQ_ALU_CONST_CACHE_PS_4
+0x00028954 SQ_ALU_CONST_CACHE_PS_5
+0x00028958 SQ_ALU_CONST_CACHE_PS_6
+0x0002895C SQ_ALU_CONST_CACHE_PS_7
+0x00028960 SQ_ALU_CONST_CACHE_PS_8
+0x00028964 SQ_ALU_CONST_CACHE_PS_9
+0x00028968 SQ_ALU_CONST_CACHE_PS_10
+0x0002896C SQ_ALU_CONST_CACHE_PS_11
+0x00028970 SQ_ALU_CONST_CACHE_PS_12
+0x00028974 SQ_ALU_CONST_CACHE_PS_13
+0x00028978 SQ_ALU_CONST_CACHE_PS_14
+0x0002897C SQ_ALU_CONST_CACHE_PS_15
+0x00028980 SQ_ALU_CONST_CACHE_VS_0
+0x00028984 SQ_ALU_CONST_CACHE_VS_1
+0x00028988 SQ_ALU_CONST_CACHE_VS_2
+0x0002898C SQ_ALU_CONST_CACHE_VS_3
+0x00028990 SQ_ALU_CONST_CACHE_VS_4
+0x00028994 SQ_ALU_CONST_CACHE_VS_5
+0x00028998 SQ_ALU_CONST_CACHE_VS_6
+0x0002899C SQ_ALU_CONST_CACHE_VS_7
+0x000289A0 SQ_ALU_CONST_CACHE_VS_8
+0x000289A4 SQ_ALU_CONST_CACHE_VS_9
+0x000289A8 SQ_ALU_CONST_CACHE_VS_10
+0x000289AC SQ_ALU_CONST_CACHE_VS_11
+0x000289B0 SQ_ALU_CONST_CACHE_VS_12
+0x000289B4 SQ_ALU_CONST_CACHE_VS_13
+0x000289B8 SQ_ALU_CONST_CACHE_VS_14
+0x000289BC SQ_ALU_CONST_CACHE_VS_15
+0x000288D8 SQ_PGM_CF_OFFSET_ES
+0x000288DC SQ_PGM_CF_OFFSET_FS
+0x000288D4 SQ_PGM_CF_OFFSET_GS
+0x000288CC SQ_PGM_CF_OFFSET_PS
+0x000288D0 SQ_PGM_CF_OFFSET_VS
+0x00028854 SQ_PGM_EXPORTS_PS
+0x00028890 SQ_PGM_RESOURCES_ES
+0x000288A4 SQ_PGM_RESOURCES_FS
+0x0002887C SQ_PGM_RESOURCES_GS
+0x00028850 SQ_PGM_RESOURCES_PS
+0x00028868 SQ_PGM_RESOURCES_VS
+0x00009100 SPI_CONFIG_CNTL
+0x0000913C SPI_CONFIG_CNTL_1
+0x000286DC SPI_FOG_CNTL
+0x000286E4 SPI_FOG_FUNC_BIAS
+0x000286E0 SPI_FOG_FUNC_SCALE
+0x000286D8 SPI_INPUT_Z
+0x000286D4 SPI_INTERP_CONTROL_0
+0x00028644 SPI_PS_INPUT_CNTL_0
+0x00028648 SPI_PS_INPUT_CNTL_1
+0x0002864C SPI_PS_INPUT_CNTL_2
+0x00028650 SPI_PS_INPUT_CNTL_3
+0x00028654 SPI_PS_INPUT_CNTL_4
+0x00028658 SPI_PS_INPUT_CNTL_5
+0x0002865C SPI_PS_INPUT_CNTL_6
+0x00028660 SPI_PS_INPUT_CNTL_7
+0x00028664 SPI_PS_INPUT_CNTL_8
+0x00028668 SPI_PS_INPUT_CNTL_9
+0x0002866C SPI_PS_INPUT_CNTL_10
+0x00028670 SPI_PS_INPUT_CNTL_11
+0x00028674 SPI_PS_INPUT_CNTL_12
+0x00028678 SPI_PS_INPUT_CNTL_13
+0x0002867C SPI_PS_INPUT_CNTL_14
+0x00028680 SPI_PS_INPUT_CNTL_15
+0x00028684 SPI_PS_INPUT_CNTL_16
+0x00028688 SPI_PS_INPUT_CNTL_17
+0x0002868C SPI_PS_INPUT_CNTL_18
+0x00028690 SPI_PS_INPUT_CNTL_19
+0x00028694 SPI_PS_INPUT_CNTL_20
+0x00028698 SPI_PS_INPUT_CNTL_21
+0x0002869C SPI_PS_INPUT_CNTL_22
+0x000286A0 SPI_PS_INPUT_CNTL_23
+0x000286A4 SPI_PS_INPUT_CNTL_24
+0x000286A8 SPI_PS_INPUT_CNTL_25
+0x000286AC SPI_PS_INPUT_CNTL_26
+0x000286B0 SPI_PS_INPUT_CNTL_27
+0x000286B4 SPI_PS_INPUT_CNTL_28
+0x000286B8 SPI_PS_INPUT_CNTL_29
+0x000286BC SPI_PS_INPUT_CNTL_30
+0x000286C0 SPI_PS_INPUT_CNTL_31
+0x000286CC SPI_PS_IN_CONTROL_0
+0x000286D0 SPI_PS_IN_CONTROL_1
+0x000286C4 SPI_VS_OUT_CONFIG
+0x00028614 SPI_VS_OUT_ID_0
+0x00028618 SPI_VS_OUT_ID_1
+0x0002861C SPI_VS_OUT_ID_2
+0x00028620 SPI_VS_OUT_ID_3
+0x00028624 SPI_VS_OUT_ID_4
+0x00028628 SPI_VS_OUT_ID_5
+0x0002862C SPI_VS_OUT_ID_6
+0x00028630 SPI_VS_OUT_ID_7
+0x00028634 SPI_VS_OUT_ID_8
+0x00028638 SPI_VS_OUT_ID_9
+0x00028438 SX_ALPHA_REF
+0x00028410 SX_ALPHA_TEST_CONTROL
+0x00028350 SX_MISC
+0x0000A020 SMX_DC_CTL0
+0x0000A024 SMX_DC_CTL1
+0x0000A028 SMX_DC_CTL2
+0x00009608 TC_CNTL
+0x00009604 TC_INVALIDATE
+0x00009490 TD_CNTL
+0x00009400 TD_FILTER4
+0x00009404 TD_FILTER4_1
+0x00009408 TD_FILTER4_2
+0x0000940C TD_FILTER4_3
+0x00009410 TD_FILTER4_4
+0x00009414 TD_FILTER4_5
+0x00009418 TD_FILTER4_6
+0x0000941C TD_FILTER4_7
+0x00009420 TD_FILTER4_8
+0x00009424 TD_FILTER4_9
+0x00009428 TD_FILTER4_10
+0x0000942C TD_FILTER4_11
+0x00009430 TD_FILTER4_12
+0x00009434 TD_FILTER4_13
+0x00009438 TD_FILTER4_14
+0x0000943C TD_FILTER4_15
+0x00009440 TD_FILTER4_16
+0x00009444 TD_FILTER4_17
+0x00009448 TD_FILTER4_18
+0x0000944C TD_FILTER4_19
+0x00009450 TD_FILTER4_20
+0x00009454 TD_FILTER4_21
+0x00009458 TD_FILTER4_22
+0x0000945C TD_FILTER4_23
+0x00009460 TD_FILTER4_24
+0x00009464 TD_FILTER4_25
+0x00009468 TD_FILTER4_26
+0x0000946C TD_FILTER4_27
+0x00009470 TD_FILTER4_28
+0x00009474 TD_FILTER4_29
+0x00009478 TD_FILTER4_30
+0x0000947C TD_FILTER4_31
+0x00009480 TD_FILTER4_32
+0x00009484 TD_FILTER4_33
+0x00009488 TD_FILTER4_34
+0x0000948C TD_FILTER4_35
+0x0000A80C TD_GS_SAMPLER0_BORDER_ALPHA
+0x0000A81C TD_GS_SAMPLER1_BORDER_ALPHA
+0x0000A82C TD_GS_SAMPLER2_BORDER_ALPHA
+0x0000A83C TD_GS_SAMPLER3_BORDER_ALPHA
+0x0000A84C TD_GS_SAMPLER4_BORDER_ALPHA
+0x0000A85C TD_GS_SAMPLER5_BORDER_ALPHA
+0x0000A86C TD_GS_SAMPLER6_BORDER_ALPHA
+0x0000A87C TD_GS_SAMPLER7_BORDER_ALPHA
+0x0000A88C TD_GS_SAMPLER8_BORDER_ALPHA
+0x0000A89C TD_GS_SAMPLER9_BORDER_ALPHA
+0x0000A8AC TD_GS_SAMPLER10_BORDER_ALPHA
+0x0000A8BC TD_GS_SAMPLER11_BORDER_ALPHA
+0x0000A8CC TD_GS_SAMPLER12_BORDER_ALPHA
+0x0000A8DC TD_GS_SAMPLER13_BORDER_ALPHA
+0x0000A8EC TD_GS_SAMPLER14_BORDER_ALPHA
+0x0000A8FC TD_GS_SAMPLER15_BORDER_ALPHA
+0x0000A90C TD_GS_SAMPLER16_BORDER_ALPHA
+0x0000A91C TD_GS_SAMPLER17_BORDER_ALPHA
+0x0000A808 TD_GS_SAMPLER0_BORDER_BLUE
+0x0000A818 TD_GS_SAMPLER1_BORDER_BLUE
+0x0000A828 TD_GS_SAMPLER2_BORDER_BLUE
+0x0000A838 TD_GS_SAMPLER3_BORDER_BLUE
+0x0000A848 TD_GS_SAMPLER4_BORDER_BLUE
+0x0000A858 TD_GS_SAMPLER5_BORDER_BLUE
+0x0000A868 TD_GS_SAMPLER6_BORDER_BLUE
+0x0000A878 TD_GS_SAMPLER7_BORDER_BLUE
+0x0000A888 TD_GS_SAMPLER8_BORDER_BLUE
+0x0000A898 TD_GS_SAMPLER9_BORDER_BLUE
+0x0000A8A8 TD_GS_SAMPLER10_BORDER_BLUE
+0x0000A8B8 TD_GS_SAMPLER11_BORDER_BLUE
+0x0000A8C8 TD_GS_SAMPLER12_BORDER_BLUE
+0x0000A8D8 TD_GS_SAMPLER13_BORDER_BLUE
+0x0000A8E8 TD_GS_SAMPLER14_BORDER_BLUE
+0x0000A8F8 TD_GS_SAMPLER15_BORDER_BLUE
+0x0000A908 TD_GS_SAMPLER16_BORDER_BLUE
+0x0000A918 TD_GS_SAMPLER17_BORDER_BLUE
+0x0000A804 TD_GS_SAMPLER0_BORDER_GREEN
+0x0000A814 TD_GS_SAMPLER1_BORDER_GREEN
+0x0000A824 TD_GS_SAMPLER2_BORDER_GREEN
+0x0000A834 TD_GS_SAMPLER3_BORDER_GREEN
+0x0000A844 TD_GS_SAMPLER4_BORDER_GREEN
+0x0000A854 TD_GS_SAMPLER5_BORDER_GREEN
+0x0000A864 TD_GS_SAMPLER6_BORDER_GREEN
+0x0000A874 TD_GS_SAMPLER7_BORDER_GREEN
+0x0000A884 TD_GS_SAMPLER8_BORDER_GREEN
+0x0000A894 TD_GS_SAMPLER9_BORDER_GREEN
+0x0000A8A4 TD_GS_SAMPLER10_BORDER_GREEN
+0x0000A8B4 TD_GS_SAMPLER11_BORDER_GREEN
+0x0000A8C4 TD_GS_SAMPLER12_BORDER_GREEN
+0x0000A8D4 TD_GS_SAMPLER13_BORDER_GREEN
+0x0000A8E4 TD_GS_SAMPLER14_BORDER_GREEN
+0x0000A8F4 TD_GS_SAMPLER15_BORDER_GREEN
+0x0000A904 TD_GS_SAMPLER16_BORDER_GREEN
+0x0000A914 TD_GS_SAMPLER17_BORDER_GREEN
+0x0000A800 TD_GS_SAMPLER0_BORDER_RED
+0x0000A810 TD_GS_SAMPLER1_BORDER_RED
+0x0000A820 TD_GS_SAMPLER2_BORDER_RED
+0x0000A830 TD_GS_SAMPLER3_BORDER_RED
+0x0000A840 TD_GS_SAMPLER4_BORDER_RED
+0x0000A850 TD_GS_SAMPLER5_BORDER_RED
+0x0000A860 TD_GS_SAMPLER6_BORDER_RED
+0x0000A870 TD_GS_SAMPLER7_BORDER_RED
+0x0000A880 TD_GS_SAMPLER8_BORDER_RED
+0x0000A890 TD_GS_SAMPLER9_BORDER_RED
+0x0000A8A0 TD_GS_SAMPLER10_BORDER_RED
+0x0000A8B0 TD_GS_SAMPLER11_BORDER_RED
+0x0000A8C0 TD_GS_SAMPLER12_BORDER_RED
+0x0000A8D0 TD_GS_SAMPLER13_BORDER_RED
+0x0000A8E0 TD_GS_SAMPLER14_BORDER_RED
+0x0000A8F0 TD_GS_SAMPLER15_BORDER_RED
+0x0000A900 TD_GS_SAMPLER16_BORDER_RED
+0x0000A910 TD_GS_SAMPLER17_BORDER_RED
+0x0000A40C TD_PS_SAMPLER0_BORDER_ALPHA
+0x0000A41C TD_PS_SAMPLER1_BORDER_ALPHA
+0x0000A42C TD_PS_SAMPLER2_BORDER_ALPHA
+0x0000A43C TD_PS_SAMPLER3_BORDER_ALPHA
+0x0000A44C TD_PS_SAMPLER4_BORDER_ALPHA
+0x0000A45C TD_PS_SAMPLER5_BORDER_ALPHA
+0x0000A46C TD_PS_SAMPLER6_BORDER_ALPHA
+0x0000A47C TD_PS_SAMPLER7_BORDER_ALPHA
+0x0000A48C TD_PS_SAMPLER8_BORDER_ALPHA
+0x0000A49C TD_PS_SAMPLER9_BORDER_ALPHA
+0x0000A4AC TD_PS_SAMPLER10_BORDER_ALPHA
+0x0000A4BC TD_PS_SAMPLER11_BORDER_ALPHA
+0x0000A4CC TD_PS_SAMPLER12_BORDER_ALPHA
+0x0000A4DC TD_PS_SAMPLER13_BORDER_ALPHA
+0x0000A4EC TD_PS_SAMPLER14_BORDER_ALPHA
+0x0000A4FC TD_PS_SAMPLER15_BORDER_ALPHA
+0x0000A50C TD_PS_SAMPLER16_BORDER_ALPHA
+0x0000A51C TD_PS_SAMPLER17_BORDER_ALPHA
+0x0000A408 TD_PS_SAMPLER0_BORDER_BLUE
+0x0000A418 TD_PS_SAMPLER1_BORDER_BLUE
+0x0000A428 TD_PS_SAMPLER2_BORDER_BLUE
+0x0000A438 TD_PS_SAMPLER3_BORDER_BLUE
+0x0000A448 TD_PS_SAMPLER4_BORDER_BLUE
+0x0000A458 TD_PS_SAMPLER5_BORDER_BLUE
+0x0000A468 TD_PS_SAMPLER6_BORDER_BLUE
+0x0000A478 TD_PS_SAMPLER7_BORDER_BLUE
+0x0000A488 TD_PS_SAMPLER8_BORDER_BLUE
+0x0000A498 TD_PS_SAMPLER9_BORDER_BLUE
+0x0000A4A8 TD_PS_SAMPLER10_BORDER_BLUE
+0x0000A4B8 TD_PS_SAMPLER11_BORDER_BLUE
+0x0000A4C8 TD_PS_SAMPLER12_BORDER_BLUE
+0x0000A4D8 TD_PS_SAMPLER13_BORDER_BLUE
+0x0000A4E8 TD_PS_SAMPLER14_BORDER_BLUE
+0x0000A4F8 TD_PS_SAMPLER15_BORDER_BLUE
+0x0000A508 TD_PS_SAMPLER16_BORDER_BLUE
+0x0000A518 TD_PS_SAMPLER17_BORDER_BLUE
+0x0000A404 TD_PS_SAMPLER0_BORDER_GREEN
+0x0000A414 TD_PS_SAMPLER1_BORDER_GREEN
+0x0000A424 TD_PS_SAMPLER2_BORDER_GREEN
+0x0000A434 TD_PS_SAMPLER3_BORDER_GREEN
+0x0000A444 TD_PS_SAMPLER4_BORDER_GREEN
+0x0000A454 TD_PS_SAMPLER5_BORDER_GREEN
+0x0000A464 TD_PS_SAMPLER6_BORDER_GREEN
+0x0000A474 TD_PS_SAMPLER7_BORDER_GREEN
+0x0000A484 TD_PS_SAMPLER8_BORDER_GREEN
+0x0000A494 TD_PS_SAMPLER9_BORDER_GREEN
+0x0000A4A4 TD_PS_SAMPLER10_BORDER_GREEN
+0x0000A4B4 TD_PS_SAMPLER11_BORDER_GREEN
+0x0000A4C4 TD_PS_SAMPLER12_BORDER_GREEN
+0x0000A4D4 TD_PS_SAMPLER13_BORDER_GREEN
+0x0000A4E4 TD_PS_SAMPLER14_BORDER_GREEN
+0x0000A4F4 TD_PS_SAMPLER15_BORDER_GREEN
+0x0000A504 TD_PS_SAMPLER16_BORDER_GREEN
+0x0000A514 TD_PS_SAMPLER17_BORDER_GREEN
+0x0000A400 TD_PS_SAMPLER0_BORDER_RED
+0x0000A410 TD_PS_SAMPLER1_BORDER_RED
+0x0000A420 TD_PS_SAMPLER2_BORDER_RED
+0x0000A430 TD_PS_SAMPLER3_BORDER_RED
+0x0000A440 TD_PS_SAMPLER4_BORDER_RED
+0x0000A450 TD_PS_SAMPLER5_BORDER_RED
+0x0000A460 TD_PS_SAMPLER6_BORDER_RED
+0x0000A470 TD_PS_SAMPLER7_BORDER_RED
+0x0000A480 TD_PS_SAMPLER8_BORDER_RED
+0x0000A490 TD_PS_SAMPLER9_BORDER_RED
+0x0000A4A0 TD_PS_SAMPLER10_BORDER_RED
+0x0000A4B0 TD_PS_SAMPLER11_BORDER_RED
+0x0000A4C0 TD_PS_SAMPLER12_BORDER_RED
+0x0000A4D0 TD_PS_SAMPLER13_BORDER_RED
+0x0000A4E0 TD_PS_SAMPLER14_BORDER_RED
+0x0000A4F0 TD_PS_SAMPLER15_BORDER_RED
+0x0000A500 TD_PS_SAMPLER16_BORDER_RED
+0x0000A510 TD_PS_SAMPLER17_BORDER_RED
+0x0000AA00 TD_PS_SAMPLER0_CLEARTYPE_KERNEL
+0x0000AA04 TD_PS_SAMPLER1_CLEARTYPE_KERNEL
+0x0000AA08 TD_PS_SAMPLER2_CLEARTYPE_KERNEL
+0x0000AA0C TD_PS_SAMPLER3_CLEARTYPE_KERNEL
+0x0000AA10 TD_PS_SAMPLER4_CLEARTYPE_KERNEL
+0x0000AA14 TD_PS_SAMPLER5_CLEARTYPE_KERNEL
+0x0000AA18 TD_PS_SAMPLER6_CLEARTYPE_KERNEL
+0x0000AA1C TD_PS_SAMPLER7_CLEARTYPE_KERNEL
+0x0000AA20 TD_PS_SAMPLER8_CLEARTYPE_KERNEL
+0x0000AA24 TD_PS_SAMPLER9_CLEARTYPE_KERNEL
+0x0000AA28 TD_PS_SAMPLER10_CLEARTYPE_KERNEL
+0x0000AA2C TD_PS_SAMPLER11_CLEARTYPE_KERNEL
+0x0000AA30 TD_PS_SAMPLER12_CLEARTYPE_KERNEL
+0x0000AA34 TD_PS_SAMPLER13_CLEARTYPE_KERNEL
+0x0000AA38 TD_PS_SAMPLER14_CLEARTYPE_KERNEL
+0x0000AA3C TD_PS_SAMPLER15_CLEARTYPE_KERNEL
+0x0000AA40 TD_PS_SAMPLER16_CLEARTYPE_KERNEL
+0x0000AA44 TD_PS_SAMPLER17_CLEARTYPE_KERNEL
+0x0000A60C TD_VS_SAMPLER0_BORDER_ALPHA
+0x0000A61C TD_VS_SAMPLER1_BORDER_ALPHA
+0x0000A62C TD_VS_SAMPLER2_BORDER_ALPHA
+0x0000A63C TD_VS_SAMPLER3_BORDER_ALPHA
+0x0000A64C TD_VS_SAMPLER4_BORDER_ALPHA
+0x0000A65C TD_VS_SAMPLER5_BORDER_ALPHA
+0x0000A66C TD_VS_SAMPLER6_BORDER_ALPHA
+0x0000A67C TD_VS_SAMPLER7_BORDER_ALPHA
+0x0000A68C TD_VS_SAMPLER8_BORDER_ALPHA
+0x0000A69C TD_VS_SAMPLER9_BORDER_ALPHA
+0x0000A6AC TD_VS_SAMPLER10_BORDER_ALPHA
+0x0000A6BC TD_VS_SAMPLER11_BORDER_ALPHA
+0x0000A6CC TD_VS_SAMPLER12_BORDER_ALPHA
+0x0000A6DC TD_VS_SAMPLER13_BORDER_ALPHA
+0x0000A6EC TD_VS_SAMPLER14_BORDER_ALPHA
+0x0000A6FC TD_VS_SAMPLER15_BORDER_ALPHA
+0x0000A70C TD_VS_SAMPLER16_BORDER_ALPHA
+0x0000A71C TD_VS_SAMPLER17_BORDER_ALPHA
+0x0000A608 TD_VS_SAMPLER0_BORDER_BLUE
+0x0000A618 TD_VS_SAMPLER1_BORDER_BLUE
+0x0000A628 TD_VS_SAMPLER2_BORDER_BLUE
+0x0000A638 TD_VS_SAMPLER3_BORDER_BLUE
+0x0000A648 TD_VS_SAMPLER4_BORDER_BLUE
+0x0000A658 TD_VS_SAMPLER5_BORDER_BLUE
+0x0000A668 TD_VS_SAMPLER6_BORDER_BLUE
+0x0000A678 TD_VS_SAMPLER7_BORDER_BLUE
+0x0000A688 TD_VS_SAMPLER8_BORDER_BLUE
+0x0000A698 TD_VS_SAMPLER9_BORDER_BLUE
+0x0000A6A8 TD_VS_SAMPLER10_BORDER_BLUE
+0x0000A6B8 TD_VS_SAMPLER11_BORDER_BLUE
+0x0000A6C8 TD_VS_SAMPLER12_BORDER_BLUE
+0x0000A6D8 TD_VS_SAMPLER13_BORDER_BLUE
+0x0000A6E8 TD_VS_SAMPLER14_BORDER_BLUE
+0x0000A6F8 TD_VS_SAMPLER15_BORDER_BLUE
+0x0000A708 TD_VS_SAMPLER16_BORDER_BLUE
+0x0000A718 TD_VS_SAMPLER17_BORDER_BLUE
+0x0000A604 TD_VS_SAMPLER0_BORDER_GREEN
+0x0000A614 TD_VS_SAMPLER1_BORDER_GREEN
+0x0000A624 TD_VS_SAMPLER2_BORDER_GREEN
+0x0000A634 TD_VS_SAMPLER3_BORDER_GREEN
+0x0000A644 TD_VS_SAMPLER4_BORDER_GREEN
+0x0000A654 TD_VS_SAMPLER5_BORDER_GREEN
+0x0000A664 TD_VS_SAMPLER6_BORDER_GREEN
+0x0000A674 TD_VS_SAMPLER7_BORDER_GREEN
+0x0000A684 TD_VS_SAMPLER8_BORDER_GREEN
+0x0000A694 TD_VS_SAMPLER9_BORDER_GREEN
+0x0000A6A4 TD_VS_SAMPLER10_BORDER_GREEN
+0x0000A6B4 TD_VS_SAMPLER11_BORDER_GREEN
+0x0000A6C4 TD_VS_SAMPLER12_BORDER_GREEN
+0x0000A6D4 TD_VS_SAMPLER13_BORDER_GREEN
+0x0000A6E4 TD_VS_SAMPLER14_BORDER_GREEN
+0x0000A6F4 TD_VS_SAMPLER15_BORDER_GREEN
+0x0000A704 TD_VS_SAMPLER16_BORDER_GREEN
+0x0000A714 TD_VS_SAMPLER17_BORDER_GREEN
+0x0000A600 TD_VS_SAMPLER0_BORDER_RED
+0x0000A610 TD_VS_SAMPLER1_BORDER_RED
+0x0000A620 TD_VS_SAMPLER2_BORDER_RED
+0x0000A630 TD_VS_SAMPLER3_BORDER_RED
+0x0000A640 TD_VS_SAMPLER4_BORDER_RED
+0x0000A650 TD_VS_SAMPLER5_BORDER_RED
+0x0000A660 TD_VS_SAMPLER6_BORDER_RED
+0x0000A670 TD_VS_SAMPLER7_BORDER_RED
+0x0000A680 TD_VS_SAMPLER8_BORDER_RED
+0x0000A690 TD_VS_SAMPLER9_BORDER_RED
+0x0000A6A0 TD_VS_SAMPLER10_BORDER_RED
+0x0000A6B0 TD_VS_SAMPLER11_BORDER_RED
+0x0000A6C0 TD_VS_SAMPLER12_BORDER_RED
+0x0000A6D0 TD_VS_SAMPLER13_BORDER_RED
+0x0000A6E0 TD_VS_SAMPLER14_BORDER_RED
+0x0000A6F0 TD_VS_SAMPLER15_BORDER_RED
+0x0000A700 TD_VS_SAMPLER16_BORDER_RED
+0x0000A710 TD_VS_SAMPLER17_BORDER_RED
+0x00009508 TA_CNTL_AUX
+0x0002802C DB_DEPTH_CLEAR
+0x00028D24 DB_HTILE_SURFACE
+0x00028D34 DB_PREFETCH_LIMIT
+0x00028D30 DB_PRELOAD_CONTROL
+0x00028D0C DB_RENDER_CONTROL
+0x00028D10 DB_RENDER_OVERRIDE
+0x0002880C DB_SHADER_CONTROL
+0x00028D2C DB_SRESULTS_COMPARE_STATE1
+0x00028430 DB_STENCILREFMASK
+0x00028434 DB_STENCILREFMASK_BF
+0x00028028 DB_STENCIL_CLEAR
+0x00028780 CB_BLEND0_CONTROL
+0x00028784 CB_BLEND1_CONTROL
+0x00028788 CB_BLEND2_CONTROL
+0x0002878C CB_BLEND3_CONTROL
+0x00028790 CB_BLEND4_CONTROL
+0x00028794 CB_BLEND5_CONTROL
+0x00028798 CB_BLEND6_CONTROL
+0x0002879C CB_BLEND7_CONTROL
+0x00028804 CB_BLEND_CONTROL
+0x00028420 CB_BLEND_ALPHA
+0x0002841C CB_BLEND_BLUE
+0x00028418 CB_BLEND_GREEN
+0x00028414 CB_BLEND_RED
+0x0002812C CB_CLEAR_ALPHA
+0x00028128 CB_CLEAR_BLUE
+0x00028124 CB_CLEAR_GREEN
+0x00028120 CB_CLEAR_RED
+0x00028C30 CB_CLRCMP_CONTROL
+0x00028C38 CB_CLRCMP_DST
+0x00028C3C CB_CLRCMP_MSK
+0x00028C34 CB_CLRCMP_SRC
+0x00028100 CB_COLOR0_MASK
+0x00028104 CB_COLOR1_MASK
+0x00028108 CB_COLOR2_MASK
+0x0002810C CB_COLOR3_MASK
+0x00028110 CB_COLOR4_MASK
+0x00028114 CB_COLOR5_MASK
+0x00028118 CB_COLOR6_MASK
+0x0002811C CB_COLOR7_MASK
+0x00028080 CB_COLOR0_VIEW
+0x00028084 CB_COLOR1_VIEW
+0x00028088 CB_COLOR2_VIEW
+0x0002808C CB_COLOR3_VIEW
+0x00028090 CB_COLOR4_VIEW
+0x00028094 CB_COLOR5_VIEW
+0x00028098 CB_COLOR6_VIEW
+0x0002809C CB_COLOR7_VIEW
+0x00028808 CB_COLOR_CONTROL
+0x0002842C CB_FOG_BLUE
+0x00028428 CB_FOG_GREEN
+0x00028424 CB_FOG_RED
+0x00008040 WAIT_UNTIL
+0x00008950 CC_GC_SHADER_PIPE_CONFIG
+0x00008954 GC_USER_SHADER_PIPE_CONFIG
+0x00009714 VC_ENHANCE
+0x00009830 DB_DEBUG
+0x00009838 DB_WATERMARKS
+0x00028D28 DB_SRESULTS_COMPARE_STATE0
+0x00028D44 DB_ALPHA_TO_MASK
+0x00009504 TA_CNTL
+0x00009700 VC_CNTL
+0x00009718 VC_CONFIG
+0x0000A02C SMX_DC_MC_INTF_CTL
index 287fcebfb4e67733ff27aef939380b5b92f3a742..626d51891ee968613efeffc6612698387b46f1d4 100644 (file)
@@ -113,6 +113,7 @@ int rs400_gart_enable(struct radeon_device *rdev)
        uint32_t size_reg;
        uint32_t tmp;
 
+       radeon_gart_restore(rdev);
        tmp = RREG32_MC(RS690_AIC_CTRL_SCRATCH);
        tmp |= RS690_DIS_OUT_OF_PCI_GART_ACCESS;
        WREG32_MC(RS690_AIC_CTRL_SCRATCH, tmp);
@@ -150,9 +151,8 @@ int rs400_gart_enable(struct radeon_device *rdev)
                WREG32(RADEON_AGP_BASE, 0xFFFFFFFF);
                WREG32(RS480_AGP_BASE_2, 0);
        }
-       tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
-       tmp = REG_SET(RS690_MC_AGP_TOP, tmp >> 16);
-       tmp |= REG_SET(RS690_MC_AGP_START, rdev->mc.gtt_location >> 16);
+       tmp = REG_SET(RS690_MC_AGP_TOP, rdev->mc.gtt_end >> 16);
+       tmp |= REG_SET(RS690_MC_AGP_START, rdev->mc.gtt_start >> 16);
        if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) {
                WREG32_MC(RS690_MCCFG_AGP_LOCATION, tmp);
                tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS;
@@ -251,14 +251,19 @@ void rs400_gpu_init(struct radeon_device *rdev)
        }
 }
 
-void rs400_vram_info(struct radeon_device *rdev)
+void rs400_mc_init(struct radeon_device *rdev)
 {
+       u64 base;
+
        rs400_gart_adjust_size(rdev);
+       rdev->mc.igp_sideport_enabled = radeon_combios_sideport_present(rdev);
        /* DDR for all card after R300 & IGP */
        rdev->mc.vram_is_ddr = true;
        rdev->mc.vram_width = 128;
-
        r100_vram_init_sizes(rdev);
+       base = (RREG32(RADEON_NB_TOM) & 0xffff) << 16;
+       radeon_vram_location(rdev, &rdev->mc, base);
+       radeon_gtt_location(rdev, &rdev->mc);
 }
 
 uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg)
@@ -362,22 +367,6 @@ static int rs400_debugfs_pcie_gart_info_init(struct radeon_device *rdev)
 #endif
 }
 
-static int rs400_mc_init(struct radeon_device *rdev)
-{
-       int r;
-       u32 tmp;
-
-       /* Setup GPU memory space */
-       tmp = RREG32(R_00015C_NB_TOM);
-       rdev->mc.vram_location = G_00015C_MC_FB_START(tmp) << 16;
-       rdev->mc.gtt_location = 0xFFFFFFFFUL;
-       r = radeon_mc_setup(rdev);
-       rdev->mc.igp_sideport_enabled = radeon_combios_sideport_present(rdev);
-       if (r)
-               return r;
-       return 0;
-}
-
 void rs400_mc_program(struct radeon_device *rdev)
 {
        struct r100_mc_save save;
@@ -516,12 +505,8 @@ int rs400_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       rs400_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = rs400_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize memory controller */
+       rs400_mc_init(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
        if (r)
index c3818562a13eb64f647ec74922605bf673bdf0e6..47f046b78c6ba9c19c93a2560179310b92f322d6 100644 (file)
 void rs600_gpu_init(struct radeon_device *rdev);
 int rs600_mc_wait_for_idle(struct radeon_device *rdev);
 
-int rs600_mc_init(struct radeon_device *rdev)
-{
-       /* read back the MC value from the hw */
-       int r;
-       u32 tmp;
-
-       /* Setup GPU memory space */
-       tmp = RREG32_MC(R_000004_MC_FB_LOCATION);
-       rdev->mc.vram_location = G_000004_MC_FB_START(tmp) << 16;
-       rdev->mc.gtt_location = 0xffffffffUL;
-       r = radeon_mc_setup(rdev);
-       rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
-       if (r)
-               return r;
-       return 0;
-}
-
 /* hpd for digital panel detect/disconnect */
 bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
 {
@@ -213,6 +196,7 @@ int rs600_gart_enable(struct radeon_device *rdev)
        r = radeon_gart_table_vram_pin(rdev);
        if (r)
                return r;
+       radeon_gart_restore(rdev);
        /* Enable bus master */
        tmp = RREG32(R_00004C_BUS_CNTL) & C_00004C_BUS_MASTER_DIS;
        WREG32(R_00004C_BUS_CNTL, tmp);
@@ -406,10 +390,14 @@ int rs600_irq_process(struct radeon_device *rdev)
                if (G_000044_SW_INT(status))
                        radeon_fence_process(rdev);
                /* Vertical blank interrupts */
-               if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int))
+               if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) {
                        drm_handle_vblank(rdev->ddev, 0);
-               if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
+                       wake_up(&rdev->irq.vblank_queue);
+               }
+               if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int)) {
                        drm_handle_vblank(rdev->ddev, 1);
+                       wake_up(&rdev->irq.vblank_queue);
+               }
                if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) {
                        queue_hotplug = true;
                        DRM_DEBUG("HPD1\n");
@@ -470,22 +458,22 @@ void rs600_gpu_init(struct radeon_device *rdev)
                dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n");
 }
 
-void rs600_vram_info(struct radeon_device *rdev)
+void rs600_mc_init(struct radeon_device *rdev)
 {
+       u64 base;
+
+       rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
+       rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
        rdev->mc.vram_is_ddr = true;
        rdev->mc.vram_width = 128;
-
        rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE);
        rdev->mc.mc_vram_size = rdev->mc.real_vram_size;
-
-       rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
-       rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
-
-       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
-               rdev->mc.mc_vram_size = rdev->mc.aper_size;
-
-       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
-               rdev->mc.real_vram_size = rdev->mc.aper_size;
+       rdev->mc.visible_vram_size = rdev->mc.aper_size;
+       rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
+       base = RREG32_MC(R_000004_MC_FB_LOCATION);
+       base = G_000004_MC_FB_START(base) << 16;
+       radeon_vram_location(rdev, &rdev->mc, base);
+       radeon_gtt_location(rdev, &rdev->mc);
 }
 
 void rs600_bandwidth_update(struct radeon_device *rdev)
@@ -661,12 +649,8 @@ int rs600_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       rs600_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = rs600_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize memory controller */
+       rs600_mc_init(rdev);
        rs600_debugfs(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
index 06e2771aee5a8443b94e44e80da3c3b7928590b5..83b9174f76f255e9fb488a3fe294a87eb0fafd62 100644 (file)
@@ -129,27 +129,21 @@ void rs690_pm_info(struct radeon_device *rdev)
        rdev->pm.sideport_bandwidth.full = rfixed_div(rdev->pm.sideport_bandwidth, tmp);
 }
 
-void rs690_vram_info(struct radeon_device *rdev)
+void rs690_mc_init(struct radeon_device *rdev)
 {
        fixed20_12 a;
+       u64 base;
 
        rs400_gart_adjust_size(rdev);
-
        rdev->mc.vram_is_ddr = true;
        rdev->mc.vram_width = 128;
-
        rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE);
        rdev->mc.mc_vram_size = rdev->mc.real_vram_size;
-
        rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
        rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
-
-       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
-               rdev->mc.mc_vram_size = rdev->mc.aper_size;
-
-       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
-               rdev->mc.real_vram_size = rdev->mc.aper_size;
-
+       rdev->mc.visible_vram_size = rdev->mc.aper_size;
+       base = RREG32_MC(R_000100_MCCFG_FB_LOCATION);
+       base = G_000100_MC_FB_START(base) << 16;
        rs690_pm_info(rdev);
        /* FIXME: we should enforce default clock in case GPU is not in
         * default setup
@@ -160,22 +154,9 @@ void rs690_vram_info(struct radeon_device *rdev)
        a.full = rfixed_const(16);
        /* core_bandwidth = sclk(Mhz) * 16 */
        rdev->pm.core_bandwidth.full = rfixed_div(rdev->pm.sclk, a);
-}
-
-static int rs690_mc_init(struct radeon_device *rdev)
-{
-       int r;
-       u32 tmp;
-
-       /* Setup GPU memory space */
-       tmp = RREG32_MC(R_000100_MCCFG_FB_LOCATION);
-       rdev->mc.vram_location = G_000100_MC_FB_START(tmp) << 16;
-       rdev->mc.gtt_location = 0xFFFFFFFFUL;
-       r = radeon_mc_setup(rdev);
        rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
-       if (r)
-               return r;
-       return 0;
+       radeon_vram_location(rdev, &rdev->mc, base);
+       radeon_gtt_location(rdev, &rdev->mc);
 }
 
 void rs690_line_buffer_adjust(struct radeon_device *rdev,
@@ -728,12 +709,8 @@ int rs690_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       rs690_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = rs690_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize memory controller */
+       rs690_mc_init(rdev);
        rv515_debugfs(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
index 0e1e6b8632b86c0e1b6d9665f831f684c11c1dcc..bea747da123ffef833df17c8d71e9ee1b0c02121 100644 (file)
@@ -277,13 +277,15 @@ static void rv515_vram_get_type(struct radeon_device *rdev)
        }
 }
 
-void rv515_vram_info(struct radeon_device *rdev)
+void rv515_mc_init(struct radeon_device *rdev)
 {
        fixed20_12 a;
 
        rv515_vram_get_type(rdev);
-
        r100_vram_init_sizes(rdev);
+       radeon_vram_location(rdev, &rdev->mc, 0);
+       if (!(rdev->flags & RADEON_IS_AGP))
+               radeon_gtt_location(rdev, &rdev->mc);
        /* FIXME: we should enforce default clock in case GPU is not in
         * default setup
         */
@@ -587,12 +589,15 @@ int rv515_init(struct radeon_device *rdev)
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
        radeon_pm_init(rdev);
-       /* Get vram informations */
-       rv515_vram_info(rdev);
-       /* Initialize memory controller (also test AGP) */
-       r = r420_mc_init(rdev);
-       if (r)
-               return r;
+       /* initialize AGP */
+       if (rdev->flags & RADEON_IS_AGP) {
+               r = radeon_agp_init(rdev);
+               if (r) {
+                       radeon_agp_disable(rdev);
+               }
+       }
+       /* initialize memory controller */
+       rv515_mc_init(rdev);
        rv515_debugfs(rdev);
        /* Fence driver */
        r = radeon_fence_driver_init(rdev);
index 03021674d097bac2605debedfff0987bfda7377b..37887dee12afc48fa999fcf4f828433396604213 100644 (file)
@@ -56,6 +56,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
        r = radeon_gart_table_vram_pin(rdev);
        if (r)
                return r;
+       radeon_gart_restore(rdev);
        /* Setup L2 cache */
        WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
                                ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
@@ -273,9 +274,10 @@ static int rv770_cp_load_microcode(struct radeon_device *rdev)
 /*
  * Core functions
  */
-static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
-                                               u32 num_backends,
-                                               u32 backend_disable_mask)
+static u32 r700_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
+                                            u32 num_tile_pipes,
+                                            u32 num_backends,
+                                            u32 backend_disable_mask)
 {
        u32 backend_map = 0;
        u32 enabled_backends_mask;
@@ -284,6 +286,7 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
        u32 swizzle_pipe[R7XX_MAX_PIPES];
        u32 cur_backend;
        u32 i;
+       bool force_no_swizzle;
 
        if (num_tile_pipes > R7XX_MAX_PIPES)
                num_tile_pipes = R7XX_MAX_PIPES;
@@ -313,6 +316,18 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
        if (enabled_backends_count != num_backends)
                num_backends = enabled_backends_count;
 
+       switch (rdev->family) {
+       case CHIP_RV770:
+       case CHIP_RV730:
+               force_no_swizzle = false;
+               break;
+       case CHIP_RV710:
+       case CHIP_RV740:
+       default:
+               force_no_swizzle = true;
+               break;
+       }
+
        memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R7XX_MAX_PIPES);
        switch (num_tile_pipes) {
        case 1:
@@ -323,49 +338,100 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
                swizzle_pipe[1] = 1;
                break;
        case 3:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 1;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 1;
+               }
                break;
        case 4:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 3;
-               swizzle_pipe[3] = 1;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 3;
+                       swizzle_pipe[3] = 1;
+               }
                break;
        case 5:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 1;
-               swizzle_pipe[4] = 3;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 1;
+                       swizzle_pipe[4] = 3;
+               }
                break;
        case 6:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 5;
-               swizzle_pipe[4] = 3;
-               swizzle_pipe[5] = 1;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+                       swizzle_pipe[5] = 5;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 5;
+                       swizzle_pipe[4] = 3;
+                       swizzle_pipe[5] = 1;
+               }
                break;
        case 7:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 6;
-               swizzle_pipe[4] = 3;
-               swizzle_pipe[5] = 1;
-               swizzle_pipe[6] = 5;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+                       swizzle_pipe[5] = 5;
+                       swizzle_pipe[6] = 6;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 6;
+                       swizzle_pipe[4] = 3;
+                       swizzle_pipe[5] = 1;
+                       swizzle_pipe[6] = 5;
+               }
                break;
        case 8:
-               swizzle_pipe[0] = 0;
-               swizzle_pipe[1] = 2;
-               swizzle_pipe[2] = 4;
-               swizzle_pipe[3] = 6;
-               swizzle_pipe[4] = 3;
-               swizzle_pipe[5] = 1;
-               swizzle_pipe[6] = 7;
-               swizzle_pipe[7] = 5;
+               if (force_no_swizzle) {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 1;
+                       swizzle_pipe[2] = 2;
+                       swizzle_pipe[3] = 3;
+                       swizzle_pipe[4] = 4;
+                       swizzle_pipe[5] = 5;
+                       swizzle_pipe[6] = 6;
+                       swizzle_pipe[7] = 7;
+               } else {
+                       swizzle_pipe[0] = 0;
+                       swizzle_pipe[1] = 2;
+                       swizzle_pipe[2] = 4;
+                       swizzle_pipe[3] = 6;
+                       swizzle_pipe[4] = 3;
+                       swizzle_pipe[5] = 1;
+                       swizzle_pipe[6] = 7;
+                       swizzle_pipe[7] = 5;
+               }
                break;
        }
 
@@ -385,8 +451,10 @@ static u32 r700_get_tile_pipe_to_backend_map(u32 num_tile_pipes,
 static void rv770_gpu_init(struct radeon_device *rdev)
 {
        int i, j, num_qd_pipes;
+       u32 ta_aux_cntl;
        u32 sx_debug_1;
        u32 smx_dc_ctl0;
+       u32 db_debug3;
        u32 num_gs_verts_per_thread;
        u32 vgt_gs_per_es;
        u32 gs_prim_buffer_depth = 0;
@@ -515,6 +583,7 @@ static void rv770_gpu_init(struct radeon_device *rdev)
 
        switch (rdev->config.rv770.max_tile_pipes) {
        case 1:
+       default:
                gb_tiling_config |= PIPE_TILING(0);
                break;
        case 2:
@@ -526,16 +595,17 @@ static void rv770_gpu_init(struct radeon_device *rdev)
        case 8:
                gb_tiling_config |= PIPE_TILING(3);
                break;
-       default:
-               break;
        }
+       rdev->config.rv770.tiling_npipes = rdev->config.rv770.max_tile_pipes;
 
        if (rdev->family == CHIP_RV770)
                gb_tiling_config |= BANK_TILING(1);
        else
                gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT);
+       rdev->config.rv770.tiling_nbanks = 4 << ((gb_tiling_config >> 4) & 0x3);
 
        gb_tiling_config |= GROUP_SIZE(0);
+       rdev->config.rv770.tiling_group_size = 256;
 
        if (((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT) > 3) {
                gb_tiling_config |= ROW_TILING(3);
@@ -549,21 +619,27 @@ static void rv770_gpu_init(struct radeon_device *rdev)
 
        gb_tiling_config |= BANK_SWAPS(1);
 
-       if (rdev->family == CHIP_RV740)
-               backend_map = 0x28;
-       else
-               backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes,
-                                                               rdev->config.rv770.max_backends,
-                                                               (0xff << rdev->config.rv770.max_backends) & 0xff);
-       gb_tiling_config |= BACKEND_MAP(backend_map);
+       cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000;
+       cc_rb_backend_disable |=
+               BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << rdev->config.rv770.max_backends) & R7XX_MAX_BACKENDS_MASK);
 
-       cc_gc_shader_pipe_config =
+       cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00;
+       cc_gc_shader_pipe_config |=
                INACTIVE_QD_PIPES((R7XX_MAX_PIPES_MASK << rdev->config.rv770.max_pipes) & R7XX_MAX_PIPES_MASK);
        cc_gc_shader_pipe_config |=
                INACTIVE_SIMDS((R7XX_MAX_SIMDS_MASK << rdev->config.rv770.max_simds) & R7XX_MAX_SIMDS_MASK);
 
-       cc_rb_backend_disable =
-               BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << rdev->config.rv770.max_backends) & R7XX_MAX_BACKENDS_MASK);
+       if (rdev->family == CHIP_RV740)
+               backend_map = 0x28;
+       else
+               backend_map = r700_get_tile_pipe_to_backend_map(rdev,
+                                                               rdev->config.rv770.max_tile_pipes,
+                                                               (R7XX_MAX_BACKENDS -
+                                                                r600_count_pipe_bits((cc_rb_backend_disable &
+                                                                                      R7XX_MAX_BACKENDS_MASK) >> 16)),
+                                                               (cc_rb_backend_disable >> 16));
+       gb_tiling_config |= BACKEND_MAP(backend_map);
+
 
        WREG32(GB_TILING_CONFIG, gb_tiling_config);
        WREG32(DCP_TILING_CONFIG, (gb_tiling_config & 0xffff));
@@ -571,16 +647,13 @@ static void rv770_gpu_init(struct radeon_device *rdev)
 
        WREG32(CC_RB_BACKEND_DISABLE,      cc_rb_backend_disable);
        WREG32(CC_GC_SHADER_PIPE_CONFIG,   cc_gc_shader_pipe_config);
-       WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config);
+       WREG32(CC_SYS_RB_BACKEND_DISABLE,  cc_rb_backend_disable);
 
-       WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable);
        WREG32(CGTS_SYS_TCC_DISABLE, 0);
        WREG32(CGTS_TCC_DISABLE, 0);
-       WREG32(CGTS_USER_SYS_TCC_DISABLE, 0);
-       WREG32(CGTS_USER_TCC_DISABLE, 0);
 
        num_qd_pipes =
-               R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK);
+               R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8);
        WREG32(VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & DEALLOC_DIST_MASK);
        WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & VTX_REUSE_DEPTH_MASK);
 
@@ -590,10 +663,8 @@ static void rv770_gpu_init(struct radeon_device *rdev)
 
        WREG32(CP_MEQ_THRESHOLDS, STQ_SPLIT(0x30));
 
-       WREG32(TA_CNTL_AUX, (DISABLE_CUBE_ANISO |
-                            SYNC_GRADIENT |
-                            SYNC_WALKER |
-                            SYNC_ALIGNER));
+       ta_aux_cntl = RREG32(TA_CNTL_AUX);
+       WREG32(TA_CNTL_AUX, ta_aux_cntl | DISABLE_CUBE_ANISO);
 
        sx_debug_1 = RREG32(SX_DEBUG_1);
        sx_debug_1 |= ENABLE_NEW_SMX_ADDRESS;
@@ -604,14 +675,28 @@ static void rv770_gpu_init(struct radeon_device *rdev)
        smx_dc_ctl0 |= CACHE_DEPTH((rdev->config.rv770.sx_num_of_sets * 64) - 1);
        WREG32(SMX_DC_CTL0, smx_dc_ctl0);
 
-       WREG32(SMX_EVENT_CTL, (ES_FLUSH_CTL(4) |
-                              GS_FLUSH_CTL(4) |
-                              ACK_FLUSH_CTL(3) |
-                              SYNC_FLUSH_CTL));
+       if (rdev->family != CHIP_RV740)
+               WREG32(SMX_EVENT_CTL, (ES_FLUSH_CTL(4) |
+                                      GS_FLUSH_CTL(4) |
+                                      ACK_FLUSH_CTL(3) |
+                                      SYNC_FLUSH_CTL));
 
-       if (rdev->family == CHIP_RV770)
-               WREG32(DB_DEBUG3, DB_CLK_OFF_DELAY(0x1f));
-       else {
+       db_debug3 = RREG32(DB_DEBUG3);
+       db_debug3 &= ~DB_CLK_OFF_DELAY(0x1f);
+       switch (rdev->family) {
+       case CHIP_RV770:
+       case CHIP_RV740:
+               db_debug3 |= DB_CLK_OFF_DELAY(0x1f);
+               break;
+       case CHIP_RV710:
+       case CHIP_RV730:
+       default:
+               db_debug3 |= DB_CLK_OFF_DELAY(2);
+               break;
+       }
+       WREG32(DB_DEBUG3, db_debug3);
+
+       if (rdev->family != CHIP_RV770) {
                db_debug4 = RREG32(DB_DEBUG4);
                db_debug4 |= DISABLE_TILE_COVERED_FOR_PS_ITER;
                WREG32(DB_DEBUG4, db_debug4);
@@ -640,10 +725,10 @@ static void rv770_gpu_init(struct radeon_device *rdev)
                            ALU_UPDATE_FIFO_HIWATER(0x8));
        switch (rdev->family) {
        case CHIP_RV770:
-               sq_ms_fifo_sizes |= FETCH_FIFO_HIWATER(0x1);
-               break;
        case CHIP_RV730:
        case CHIP_RV710:
+               sq_ms_fifo_sizes |= FETCH_FIFO_HIWATER(0x1);
+               break;
        case CHIP_RV740:
        default:
                sq_ms_fifo_sizes |= FETCH_FIFO_HIWATER(0x4);
@@ -816,45 +901,13 @@ int rv770_mc_init(struct radeon_device *rdev)
        /* Setup GPU memory space */
        rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE);
        rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE);
-
-       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
+       rdev->mc.visible_vram_size = rdev->mc.aper_size;
+       /* FIXME remove this once we support unmappable VRAM */
+       if (rdev->mc.mc_vram_size > rdev->mc.aper_size) {
                rdev->mc.mc_vram_size = rdev->mc.aper_size;
-
-       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
                rdev->mc.real_vram_size = rdev->mc.aper_size;
-
-       if (rdev->flags & RADEON_IS_AGP) {
-               /* gtt_size is setup by radeon_agp_init */
-               rdev->mc.gtt_location = rdev->mc.agp_base;
-               tmp = 0xFFFFFFFFUL - rdev->mc.agp_base - rdev->mc.gtt_size;
-               /* Try to put vram before or after AGP because we
-                * we want SYSTEM_APERTURE to cover both VRAM and
-                * AGP so that GPU can catch out of VRAM/AGP access
-                */
-               if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
-                       /* Enough place before */
-                       rdev->mc.vram_location = rdev->mc.gtt_location -
-                                                       rdev->mc.mc_vram_size;
-               } else if (tmp > rdev->mc.mc_vram_size) {
-                       /* Enough place after */
-                       rdev->mc.vram_location = rdev->mc.gtt_location +
-                                                       rdev->mc.gtt_size;
-               } else {
-                       /* Try to setup VRAM then AGP might not
-                        * not work on some card
-                        */
-                       rdev->mc.vram_location = 0x00000000UL;
-                       rdev->mc.gtt_location = rdev->mc.mc_vram_size;
-               }
-       } else {
-               rdev->mc.vram_location = 0x00000000UL;
-               rdev->mc.gtt_location = rdev->mc.mc_vram_size;
-               rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
        }
-       rdev->mc.vram_start = rdev->mc.vram_location;
-       rdev->mc.vram_end = rdev->mc.vram_location + rdev->mc.mc_vram_size - 1;
-       rdev->mc.gtt_start = rdev->mc.gtt_location;
-       rdev->mc.gtt_end = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
+       r600_vram_gtt_location(rdev, &rdev->mc);
        /* FIXME: we should enforce default clock in case GPU is not in
         * default setup
         */
@@ -863,6 +916,7 @@ int rv770_mc_init(struct radeon_device *rdev)
        rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a);
        return 0;
 }
+
 int rv770_gpu_reset(struct radeon_device *rdev)
 {
        /* FIXME: implement any rv770 specific bits */
@@ -1038,6 +1092,7 @@ int rv770_init(struct radeon_device *rdev)
        r = radeon_fence_driver_init(rdev);
        if (r)
                return r;
+       /* initialize AGP */
        if (rdev->flags & RADEON_IS_AGP) {
                r = radeon_agp_init(rdev);
                if (r)
index a1367ab6f2617353115d76d898409c00a61ad6f9..9506f8cb99e0552a295bef5bd60dfba49bfaaee0 100644 (file)
 
 #define        WAIT_UNTIL                                      0x8040
 
+#define        SRBM_STATUS                                     0x0E50
+
 #endif
index 3d47a2c12322173fa3621e9cb5886408dcb1c19e..a759170763bb6bc2e878a26834f7aa665dff943e 100644 (file)
@@ -480,7 +480,7 @@ static int ttm_tt_swapin(struct ttm_tt *ttm)
        void *from_virtual;
        void *to_virtual;
        int i;
-       int ret;
+       int ret = -ENOMEM;
 
        if (ttm->page_flags & TTM_PAGE_FLAG_USER) {
                ret = ttm_tt_set_user(ttm, ttm->tsk, ttm->start,
@@ -499,8 +499,10 @@ static int ttm_tt_swapin(struct ttm_tt *ttm)
 
        for (i = 0; i < ttm->num_pages; ++i) {
                from_page = read_mapping_page(swap_space, i, NULL);
-               if (IS_ERR(from_page))
+               if (IS_ERR(from_page)) {
+                       ret = PTR_ERR(from_page);
                        goto out_err;
+               }
                to_page = __ttm_tt_get_page(ttm, i);
                if (unlikely(to_page == NULL))
                        goto out_err;
@@ -523,7 +525,7 @@ static int ttm_tt_swapin(struct ttm_tt *ttm)
        return 0;
 out_err:
        ttm_tt_free_alloced_pages(ttm);
-       return -ENOMEM;
+       return ret;
 }
 
 int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage)
@@ -535,6 +537,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage)
        void *from_virtual;
        void *to_virtual;
        int i;
+       int ret = -ENOMEM;
 
        BUG_ON(ttm->state != tt_unbound && ttm->state != tt_unpopulated);
        BUG_ON(ttm->caching_state != tt_cached);
@@ -557,7 +560,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage)
                                                0);
                if (unlikely(IS_ERR(swap_storage))) {
                        printk(KERN_ERR "Failed allocating swap storage.\n");
-                       return -ENOMEM;
+                       return PTR_ERR(swap_storage);
                }
        } else
                swap_storage = persistant_swap_storage;
@@ -569,9 +572,10 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage)
                if (unlikely(from_page == NULL))
                        continue;
                to_page = read_mapping_page(swap_space, i, NULL);
-               if (unlikely(to_page == NULL))
+               if (unlikely(IS_ERR(to_page))) {
+                       ret = PTR_ERR(to_page);
                        goto out_err;
-
+               }
                preempt_disable();
                from_virtual = kmap_atomic(from_page, KM_USER0);
                to_virtual = kmap_atomic(to_page, KM_USER1);
@@ -595,5 +599,5 @@ out_err:
        if (!persistant_swap_storage)
                fput(swap_storage);
 
-       return -ENOMEM;
+       return ret;
 }
index 0920492cea0a4af77d9ea6ec13c937a4f6ae60bc..61ab4daf0bbb9adb18190e1ff16617183bb5d1f0 100644 (file)
@@ -16,3 +16,14 @@ config VGA_ARB_MAX_GPUS
        help
          Reserves space in the kernel to maintain resource locking for
          multiple GPUS.  The overhead for each GPU is very small.
+
+config VGA_SWITCHEROO
+       bool "Laptop Hybrid Grapics - GPU switching support"
+       depends on X86
+       depends on ACPI
+       help
+         Many laptops released in 2008/9/10 have two gpus with a multiplxer
+         to switch between them. This adds support for dynamic switching when
+          X isn't running and delayed switching until the next logoff. This
+         features is called hybrid graphics, ATI PowerXpress, and Nvidia
+         HybridPower.
index 7cc8c1ed645b3f042ef2ac6630929539d1e44092..14ca30b75d0aacdf099cf936083453f088b6343c 100644 (file)
@@ -1 +1,2 @@
 obj-$(CONFIG_VGA_ARB)  += vgaarb.o
+obj-$(CONFIG_VGA_SWITCHEROO) += vga_switcheroo.o
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
new file mode 100644 (file)
index 0000000..d6d1149
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2010 Red Hat Inc.
+ * Author : Dave Airlie <airlied@redhat.com>
+ *
+ *
+ * Licensed under GPLv2
+ *
+ * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs
+
+ Switcher interface - methods require for ATPX and DCM
+ - switchto - this throws the output MUX switch
+ - discrete_set_power - sets the power state for the discrete card
+
+ GPU driver interface
+ - set_gpu_state - this should do the equiv of s/r for the card
+                 - this should *not* set the discrete power state
+ - switch_check  - check if the device is in a position to switch now
+ */
+
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/fb.h>
+
+#include <linux/pci.h>
+#include <linux/vga_switcheroo.h>
+
+struct vga_switcheroo_client {
+       struct pci_dev *pdev;
+       struct fb_info *fb_info;
+       int pwr_state;
+       void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state);
+       bool (*can_switch)(struct pci_dev *pdev);
+       int id;
+       bool active;
+};
+
+static DEFINE_MUTEX(vgasr_mutex);
+
+struct vgasr_priv {
+
+       bool active;
+       bool delayed_switch_active;
+       enum vga_switcheroo_client_id delayed_client_id;
+
+       struct dentry *debugfs_root;
+       struct dentry *switch_file;
+
+       int registered_clients;
+       struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS];
+
+       struct vga_switcheroo_handler *handler;
+};
+
+static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv);
+static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);
+
+/* only one switcheroo per system */
+static struct vgasr_priv vgasr_priv;
+
+int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
+{
+       mutex_lock(&vgasr_mutex);
+       if (vgasr_priv.handler) {
+               mutex_unlock(&vgasr_mutex);
+               return -EINVAL;
+       }
+
+       vgasr_priv.handler = handler;
+       mutex_unlock(&vgasr_mutex);
+       return 0;
+}
+EXPORT_SYMBOL(vga_switcheroo_register_handler);
+
+void vga_switcheroo_unregister_handler(void)
+{
+       mutex_lock(&vgasr_mutex);
+       vgasr_priv.handler = NULL;
+       mutex_unlock(&vgasr_mutex);
+}
+EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
+
+static void vga_switcheroo_enable(void)
+{
+       int i;
+       int ret;
+       /* call the handler to init */
+       vgasr_priv.handler->init();
+
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev);
+               if (ret < 0)
+                       return;
+
+               vgasr_priv.clients[i].id = ret;
+       }
+       vga_switcheroo_debugfs_init(&vgasr_priv);
+       vgasr_priv.active = true;
+}
+
+int vga_switcheroo_register_client(struct pci_dev *pdev,
+                                  void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state),
+                                  bool (*can_switch)(struct pci_dev *pdev))
+{
+       int index;
+
+       mutex_lock(&vgasr_mutex);
+       /* don't do IGD vs DIS here */
+       if (vgasr_priv.registered_clients & 1)
+               index = 1;
+       else
+               index = 0;
+
+       vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON;
+       vgasr_priv.clients[index].pdev = pdev;
+       vgasr_priv.clients[index].set_gpu_state = set_gpu_state;
+       vgasr_priv.clients[index].can_switch = can_switch;
+       vgasr_priv.clients[index].id = -1;
+       if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
+               vgasr_priv.clients[index].active = true;
+
+       vgasr_priv.registered_clients |= (1 << index);
+
+       /* if we get two clients + handler */
+       if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) {
+               printk(KERN_INFO "vga_switcheroo: enabled\n");
+               vga_switcheroo_enable();
+       }
+       mutex_unlock(&vgasr_mutex);
+       return 0;
+}
+EXPORT_SYMBOL(vga_switcheroo_register_client);
+
+void vga_switcheroo_unregister_client(struct pci_dev *pdev)
+{
+       int i;
+
+       mutex_lock(&vgasr_mutex);
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               if (vgasr_priv.clients[i].pdev == pdev) {
+                       vgasr_priv.registered_clients &= ~(1 << i);
+                       break;
+               }
+       }
+
+       printk(KERN_INFO "vga_switcheroo: disabled\n");
+       vga_switcheroo_debugfs_fini(&vgasr_priv);
+       vgasr_priv.active = false;
+       mutex_unlock(&vgasr_mutex);
+}
+EXPORT_SYMBOL(vga_switcheroo_unregister_client);
+
+void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
+                                struct fb_info *info)
+{
+       int i;
+
+       mutex_lock(&vgasr_mutex);
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               if (vgasr_priv.clients[i].pdev == pdev) {
+                       vgasr_priv.clients[i].fb_info = info;
+                       break;
+               }
+       }
+       mutex_unlock(&vgasr_mutex);
+}
+EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
+
+static int vga_switcheroo_show(struct seq_file *m, void *v)
+{
+       int i;
+       mutex_lock(&vgasr_mutex);
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               seq_printf(m, "%d:%c:%s:%s\n", i,
+                          vgasr_priv.clients[i].active ? '+' : ' ',
+                          vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off",
+                          pci_name(vgasr_priv.clients[i].pdev));
+       }
+       mutex_unlock(&vgasr_mutex);
+       return 0;
+}
+
+static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, vga_switcheroo_show, NULL);
+}
+
+static int vga_switchon(struct vga_switcheroo_client *client)
+{
+       int ret;
+
+       ret = vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
+       /* call the driver callback to turn on device */
+       client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON);
+       client->pwr_state = VGA_SWITCHEROO_ON;
+       return 0;
+}
+
+static int vga_switchoff(struct vga_switcheroo_client *client)
+{
+       /* call the driver callback to turn off device */
+       client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
+       vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF);
+       client->pwr_state = VGA_SWITCHEROO_OFF;
+       return 0;
+}
+
+static int vga_switchto(struct vga_switcheroo_client *new_client)
+{
+       int ret;
+       int i;
+       struct vga_switcheroo_client *active = NULL;
+
+       if (new_client->active == true)
+               return 0;
+
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               if (vgasr_priv.clients[i].active == true) {
+                       active = &vgasr_priv.clients[i];
+                       break;
+               }
+       }
+       if (!active)
+               return 0;
+
+       /* power up the first device */
+       ret = pci_enable_device(new_client->pdev);
+       if (ret)
+               return ret;
+
+       if (new_client->pwr_state == VGA_SWITCHEROO_OFF)
+               vga_switchon(new_client);
+
+       /* swap shadow resource to denote boot VGA device has changed so X starts on new device */
+       active->active = false;
+
+       active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW;
+       new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+
+       if (new_client->fb_info) {
+               struct fb_event event;
+               event.info = new_client->fb_info;
+               fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);
+       }
+
+       ret = vgasr_priv.handler->switchto(new_client->id);
+       if (ret)
+               return ret;
+
+       if (active->pwr_state == VGA_SWITCHEROO_ON)
+               vga_switchoff(active);
+
+       new_client->active = true;
+       return 0;
+}
+
+static ssize_t
+vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
+                            size_t cnt, loff_t *ppos)
+{
+       char usercmd[64];
+       const char *pdev_name;
+       int i, ret;
+       bool delay = false, can_switch;
+       int client_id = -1;
+       struct vga_switcheroo_client *client = NULL;
+
+       if (cnt > 63)
+               cnt = 63;
+
+       if (copy_from_user(usercmd, ubuf, cnt))
+               return -EFAULT;
+
+       mutex_lock(&vgasr_mutex);
+
+       if (!vgasr_priv.active)
+               return -EINVAL;
+
+       /* pwr off the device not in use */
+       if (strncmp(usercmd, "OFF", 3) == 0) {
+               for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+                       if (vgasr_priv.clients[i].active)
+                               continue;
+                       if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON)
+                               vga_switchoff(&vgasr_priv.clients[i]);
+               }
+               goto out;
+       }
+       /* pwr on the device not in use */
+       if (strncmp(usercmd, "ON", 2) == 0) {
+               for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+                       if (vgasr_priv.clients[i].active)
+                               continue;
+                       if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF)
+                               vga_switchon(&vgasr_priv.clients[i]);
+               }
+               goto out;
+       }
+
+       /* request a delayed switch - test can we switch now */
+       if (strncmp(usercmd, "DIGD", 4) == 0) {
+               client_id = VGA_SWITCHEROO_IGD;
+               delay = true;
+       }
+
+       if (strncmp(usercmd, "DDIS", 4) == 0) {
+               client_id = VGA_SWITCHEROO_DIS;
+               delay = true;
+       }
+
+       if (strncmp(usercmd, "IGD", 3) == 0)
+               client_id = VGA_SWITCHEROO_IGD;
+
+       if (strncmp(usercmd, "DIS", 3) == 0)
+               client_id = VGA_SWITCHEROO_DIS;
+
+       if (client_id == -1)
+               goto out;
+
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               if (vgasr_priv.clients[i].id == client_id) {
+                       client = &vgasr_priv.clients[i];
+                       break;
+               }
+       }
+
+       vgasr_priv.delayed_switch_active = false;
+       /* okay we want a switch - test if devices are willing to switch */
+       can_switch = true;
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);
+               if (can_switch == false) {
+                       printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i);
+                       break;
+               }
+       }
+
+       if (can_switch == false && delay == false)
+               goto out;
+
+       if (can_switch == true) {
+               pdev_name = pci_name(client->pdev);
+               ret = vga_switchto(client);
+               if (ret)
+                       printk(KERN_ERR "vga_switcheroo: switching failed %d\n", ret);
+       } else {
+               printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id);
+               vgasr_priv.delayed_switch_active = true;
+               vgasr_priv.delayed_client_id = client_id;
+
+               /* we should at least power up the card to
+                  make the switch faster */
+               if (client->pwr_state == VGA_SWITCHEROO_OFF)
+                       vga_switchon(client);
+       }
+
+out:
+       mutex_unlock(&vgasr_mutex);
+       return cnt;
+}
+
+static const struct file_operations vga_switcheroo_debugfs_fops = {
+       .owner = THIS_MODULE,
+       .open = vga_switcheroo_debugfs_open,
+       .write = vga_switcheroo_debugfs_write,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv)
+{
+       if (priv->switch_file) {
+               debugfs_remove(priv->switch_file);
+               priv->switch_file = NULL;
+       }
+       if (priv->debugfs_root) {
+               debugfs_remove(priv->debugfs_root);
+               priv->debugfs_root = NULL;
+       }
+}
+
+static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv)
+{
+       /* already initialised */
+       if (priv->debugfs_root)
+               return 0;
+       priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL);
+
+       if (!priv->debugfs_root) {
+               printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n");
+               goto fail;
+       }
+
+       priv->switch_file = debugfs_create_file("switch", 0644,
+                                               priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops);
+       if (!priv->switch_file) {
+               printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n");
+               goto fail;
+       }
+       return 0;
+fail:
+       vga_switcheroo_debugfs_fini(priv);
+       return -1;
+}
+
+int vga_switcheroo_process_delayed_switch(void)
+{
+       struct vga_switcheroo_client *client = NULL;
+       const char *pdev_name;
+       bool can_switch = true;
+       int i;
+       int ret;
+       int err = -EINVAL;
+
+       mutex_lock(&vgasr_mutex);
+       if (!vgasr_priv.delayed_switch_active)
+               goto err;
+
+       printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id);
+
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id)
+                       client = &vgasr_priv.clients[i];
+               can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);
+               if (can_switch == false) {
+                       printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i);
+                       break;
+               }
+       }
+
+       if (can_switch == false || client == NULL)
+               goto err;
+
+       pdev_name = pci_name(client->pdev);
+       ret = vga_switchto(client);
+       if (ret)
+               printk(KERN_ERR "vga_switcheroo: delayed switching failed %d\n", ret);
+
+       vgasr_priv.delayed_switch_active = false;
+       err = 0;
+err:
+       mutex_unlock(&vgasr_mutex);
+       return err;
+}
+EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
+
index 68cf87749a42f9be49e6ac8be6bb4d1669612d9b..e4595e6147b420de2a4545d45e498d5d5e886ba4 100644 (file)
@@ -170,6 +170,16 @@ config SENSORS_ADM9240
          This driver can also be built as a module.  If so, the module
          will be called adm9240.
 
+config SENSORS_ADT7411
+       tristate "Analog Devices ADT7411"
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for the Analog Devices
+         ADT7411 voltage and temperature monitoring chip.
+
+         This driver can also be built as a module. If so, the module
+         will be called adt7411.
+
 config SENSORS_ADT7462
        tristate "Analog Devices ADT7462"
        depends on I2C && EXPERIMENTAL
@@ -190,20 +200,6 @@ config SENSORS_ADT7470
          This driver can also be built as a module. If so, the module
          will be called adt7470.
 
-config SENSORS_ADT7473
-       tristate "Analog Devices ADT7473 (DEPRECATED)"
-       depends on I2C && EXPERIMENTAL
-       select SENSORS_ADT7475
-       help
-         If you say yes here you get support for the Analog Devices
-         ADT7473 temperature monitoring chips.
-
-         This driver is deprecated, you should use the adt7475 driver
-         instead.
-
-         This driver can also be built as a module. If so, the module
-         will be called adt7473.
-
 config SENSORS_ADT7475
        tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490"
        depends on I2C && EXPERIMENTAL
@@ -216,6 +212,19 @@ config SENSORS_ADT7475
          This driver can also be build as a module.  If so, the module
          will be called adt7475.
 
+config SENSORS_ASC7621
+       tristate "Andigilog aSC7621"
+       depends on HWMON && I2C
+       help
+         If you say yes here you get support for the aSC7621
+         family of SMBus sensors chip found on most Intel X48, X38, 975,
+         965 and 945 desktop boards.  Currently supported chips:
+         aSC7621
+         aSC7621a
+
+         This driver can also be built as a module.  If so, the module
+         will be called asc7621.
+
 config SENSORS_K8TEMP
        tristate "AMD Athlon64/FX or Opteron temperature sensor"
        depends on X86 && PCI && EXPERIMENTAL
@@ -563,9 +572,10 @@ config SENSORS_LM90
        depends on I2C
        help
          If you say yes here you get support for National Semiconductor LM90,
-         LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim
+         LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim
          MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
-         MAX6680, MAX6681 and MAX6692 sensor chips.
+         MAX6680, MAX6681 and MAX6692, and Winbond/Nuvoton W83L771AWG/ASG
+         sensor chips.
 
          This driver can also be built as a module.  If so, the module
          will be called lm90.
@@ -909,7 +919,8 @@ config SENSORS_W83793
        select HWMON_VID
        help
          If you say yes here you get support for the Winbond W83793
-         hardware monitoring chip.
+         hardware monitoring chip, including support for the integrated
+         watchdog.
 
          This driver can also be built as a module.  If so, the module
          will be called w83793.
index 4bc215c0953f08cf515c5bb8ca3d01c192c6346f..4aa1a3d112ad99a3894a75a558b5d85049805f69 100644 (file)
@@ -29,12 +29,13 @@ obj-$(CONFIG_SENSORS_ADM1029)       += adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)  += adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)  += adm9240.o
 obj-$(CONFIG_SENSORS_ADS7828)  += ads7828.o
+obj-$(CONFIG_SENSORS_ADT7411)  += adt7411.o
 obj-$(CONFIG_SENSORS_ADT7462)  += adt7462.o
 obj-$(CONFIG_SENSORS_ADT7470)  += adt7470.o
-obj-$(CONFIG_SENSORS_ADT7473)  += adt7473.o
 obj-$(CONFIG_SENSORS_ADT7475)  += adt7475.o
 obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
 obj-$(CONFIG_SENSORS_AMS)      += ams/
+obj-$(CONFIG_SENSORS_ASC7621)  += asc7621.o
 obj-$(CONFIG_SENSORS_ATXP1)    += atxp1.o
 obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
 obj-$(CONFIG_SENSORS_DME1737)  += dme1737.o
index 5e9e095f11369cc14102ca510a4e0d5ca066d1da..74d9c5195e446ce59242be8f7b1ccb474a151dfd 100644 (file)
@@ -62,18 +62,23 @@ static ssize_t adcxx_read(struct device *dev,
        struct spi_device *spi = to_spi_device(dev);
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
        struct adcxx *adc = dev_get_drvdata(&spi->dev);
-       u8 tx_buf[2] = { attr->index << 3 }; /* other bits are don't care */
+       u8 tx_buf[2];
        u8 rx_buf[2];
        int status;
-       int value;
+       u32 value;
 
        if (mutex_lock_interruptible(&adc->lock))
                return -ERESTARTSYS;
 
-       status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf),
-                                       rx_buf, sizeof(rx_buf));
+       if (adc->channels == 1) {
+               status = spi_read(spi, rx_buf, sizeof(rx_buf));
+       } else {
+               tx_buf[0] = attr->index << 3; /* other bits are don't care */
+               status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf),
+                                               rx_buf, sizeof(rx_buf));
+       }
        if (status < 0) {
-               dev_warn(dev, "spi_write_then_read failed with status %d\n",
+               dev_warn(dev, "SPI synch. transfer failed with status %d\n",
                                status);
                goto out;
        }
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
new file mode 100644 (file)
index 0000000..3471884
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ *  Driver for the ADT7411 (I2C/SPI 8 channel 10 bit ADC & temperature-sensor)
+ *
+ *  Copyright (C) 2008, 2010 Pengutronix
+ *
+ *  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.
+ *
+ *  TODO: SPI, support for external temperature sensor
+ *       use power-down mode for suspend?, interrupt handling?
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#define ADT7411_REG_INT_TEMP_VDD_LSB           0x03
+#define ADT7411_REG_EXT_TEMP_AIN14_LSB         0x04
+#define ADT7411_REG_VDD_MSB                    0x06
+#define ADT7411_REG_INT_TEMP_MSB               0x07
+#define ADT7411_REG_EXT_TEMP_AIN1_MSB          0x08
+
+#define ADT7411_REG_CFG1                       0x18
+#define ADT7411_CFG1_START_MONITOR             (1 << 0)
+
+#define ADT7411_REG_CFG2                       0x19
+#define ADT7411_CFG2_DISABLE_AVG               (1 << 5)
+
+#define ADT7411_REG_CFG3                       0x1a
+#define ADT7411_CFG3_ADC_CLK_225               (1 << 0)
+#define ADT7411_CFG3_REF_VDD                   (1 << 4)
+
+#define ADT7411_REG_DEVICE_ID                  0x4d
+#define ADT7411_REG_MANUFACTURER_ID            0x4e
+
+#define ADT7411_DEVICE_ID                      0x2
+#define ADT7411_MANUFACTURER_ID                        0x41
+
+static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END };
+
+struct adt7411_data {
+       struct mutex device_lock;       /* for "atomic" device accesses */
+       struct mutex update_lock;
+       unsigned long next_update;
+       int vref_cached;
+       struct device *hwmon_dev;
+};
+
+/*
+ * When reading a register containing (up to 4) lsb, all associated
+ * msb-registers get locked by the hardware. After _one_ of those msb is read,
+ * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb
+ * is protected here with a mutex, too.
+ */
+static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg,
+                               u8 msb_reg, u8 lsb_shift)
+{
+       struct adt7411_data *data = i2c_get_clientdata(client);
+       int val, tmp;
+
+       mutex_lock(&data->device_lock);
+
+       val = i2c_smbus_read_byte_data(client, lsb_reg);
+       if (val < 0)
+               goto exit_unlock;
+
+       tmp = (val >> lsb_shift) & 3;
+       val = i2c_smbus_read_byte_data(client, msb_reg);
+
+       if (val >= 0)
+               val = (val << 2) | tmp;
+
+ exit_unlock:
+       mutex_unlock(&data->device_lock);
+
+       return val;
+}
+
+static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit,
+                               bool flag)
+{
+       struct adt7411_data *data = i2c_get_clientdata(client);
+       int ret, val;
+
+       mutex_lock(&data->device_lock);
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+       if (ret < 0)
+               goto exit_unlock;
+
+       if (flag)
+               val = ret | bit;
+       else
+               val = ret & ~bit;
+
+       ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ exit_unlock:
+       mutex_unlock(&data->device_lock);
+       return ret;
+}
+
+static ssize_t adt7411_show_vdd(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
+                       ADT7411_REG_VDD_MSB, 2);
+
+       return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024);
+}
+
+static ssize_t adt7411_show_temp(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
+                       ADT7411_REG_INT_TEMP_MSB, 0);
+
+       if (val < 0)
+               return val;
+
+       val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */
+
+       return sprintf(buf, "%d\n", val * 250);
+}
+
+static ssize_t adt7411_show_input(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7411_data *data = i2c_get_clientdata(client);
+       int val;
+       u8 lsb_reg, lsb_shift;
+
+       mutex_lock(&data->update_lock);
+       if (time_after_eq(jiffies, data->next_update)) {
+               val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3);
+               if (val < 0)
+                       goto exit_unlock;
+
+               if (val & ADT7411_CFG3_REF_VDD) {
+                       val = adt7411_read_10_bit(client,
+                                       ADT7411_REG_INT_TEMP_VDD_LSB,
+                                       ADT7411_REG_VDD_MSB, 2);
+                       if (val < 0)
+                               goto exit_unlock;
+
+                       data->vref_cached = val * 7000 / 1024;
+               } else {
+                       data->vref_cached = 2250;
+               }
+
+               data->next_update = jiffies + HZ;
+       }
+
+       lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2);
+       lsb_shift = 2 * (nr & 0x03);
+       val = adt7411_read_10_bit(client, lsb_reg,
+                       ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift);
+       if (val < 0)
+               goto exit_unlock;
+
+       val = sprintf(buf, "%u\n", val * data->vref_cached / 1024);
+ exit_unlock:
+       mutex_unlock(&data->update_lock);
+       return val;
+}
+
+static ssize_t adt7411_show_bit(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
+       struct i2c_client *client = to_i2c_client(dev);
+       int ret = i2c_smbus_read_byte_data(client, attr2->index);
+
+       return ret < 0 ? ret : sprintf(buf, "%u\n", !!(ret & attr2->nr));
+}
+
+static ssize_t adt7411_set_bit(struct device *dev,
+                              struct device_attribute *attr, const char *buf,
+                              size_t count)
+{
+       struct sensor_device_attribute_2 *s_attr2 = to_sensor_dev_attr_2(attr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7411_data *data = i2c_get_clientdata(client);
+       int ret;
+       unsigned long flag;
+
+       ret = strict_strtoul(buf, 0, &flag);
+       if (ret || flag > 1)
+               return -EINVAL;
+
+       ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag);
+
+       /* force update */
+       mutex_lock(&data->update_lock);
+       data->next_update = jiffies;
+       mutex_unlock(&data->update_lock);
+
+       return ret < 0 ? ret : count;
+}
+
+#define ADT7411_BIT_ATTR(__name, __reg, __bit) \
+       SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
+       adt7411_set_bit, __bit, __reg)
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL);
+static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7);
+static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG);
+static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225);
+static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
+
+static struct attribute *adt7411_attrs[] = {
+       &dev_attr_temp1_input.attr,
+       &dev_attr_in0_input.attr,
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in4_input.dev_attr.attr,
+       &sensor_dev_attr_in5_input.dev_attr.attr,
+       &sensor_dev_attr_in6_input.dev_attr.attr,
+       &sensor_dev_attr_in7_input.dev_attr.attr,
+       &sensor_dev_attr_in8_input.dev_attr.attr,
+       &sensor_dev_attr_no_average.dev_attr.attr,
+       &sensor_dev_attr_fast_sampling.dev_attr.attr,
+       &sensor_dev_attr_adc_ref_vdd.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group adt7411_attr_grp = {
+       .attrs = adt7411_attrs,
+};
+
+static int adt7411_detect(struct i2c_client *client,
+                         struct i2c_board_info *info)
+{
+       int val;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID);
+       if (val < 0 || val != ADT7411_MANUFACTURER_ID) {
+               dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, "
+                       "expected %d\n", val, ADT7411_MANUFACTURER_ID);
+               return -ENODEV;
+       }
+
+       val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID);
+       if (val < 0 || val != ADT7411_DEVICE_ID) {
+               dev_dbg(&client->dev, "Wrong device ID. Got %d, "
+                       "expected %d\n", val, ADT7411_DEVICE_ID);
+               return -ENODEV;
+       }
+
+       strlcpy(info->type, "adt7411", I2C_NAME_SIZE);
+
+       return 0;
+}
+
+static int __devinit adt7411_probe(struct i2c_client *client,
+                                  const struct i2c_device_id *id)
+{
+       struct adt7411_data *data;
+       int ret;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->device_lock);
+       mutex_init(&data->update_lock);
+
+       ret = adt7411_modify_bit(client, ADT7411_REG_CFG1,
+                                ADT7411_CFG1_START_MONITOR, 1);
+       if (ret < 0)
+               goto exit_free;
+
+       /* force update on first occasion */
+       data->next_update = jiffies;
+
+       ret = sysfs_create_group(&client->dev.kobj, &adt7411_attr_grp);
+       if (ret)
+               goto exit_free;
+
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               ret = PTR_ERR(data->hwmon_dev);
+               goto exit_remove;
+       }
+
+       dev_info(&client->dev, "successfully registered\n");
+
+       return 0;
+
+ exit_remove:
+       sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp);
+ exit_free:
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+       return ret;
+}
+
+static int __devexit adt7411_remove(struct i2c_client *client)
+{
+       struct adt7411_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp);
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+       return 0;
+}
+
+static const struct i2c_device_id adt7411_id[] = {
+       { "adt7411", 0 },
+       { }
+};
+
+static struct i2c_driver adt7411_driver = {
+       .driver         = {
+               .name           = "adt7411",
+       },
+       .probe  = adt7411_probe,
+       .remove = __devexit_p(adt7411_remove),
+       .id_table = adt7411_id,
+       .detect = adt7411_detect,
+       .address_list = normal_i2c,
+       .class = I2C_CLASS_HWMON,
+};
+
+static int __init sensors_adt7411_init(void)
+{
+       return i2c_add_driver(&adt7411_driver);
+}
+module_init(sensors_adt7411_init)
+
+static void __exit sensors_adt7411_exit(void)
+{
+       i2c_del_driver(&adt7411_driver);
+}
+module_exit(sensors_adt7411_exit)
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and "
+       "Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_DESCRIPTION("ADT7411 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c
deleted file mode 100644 (file)
index 434576f..0000000
+++ /dev/null
@@ -1,1180 +0,0 @@
-/*
- * A hwmon driver for the Analog Devices ADT7473
- * Copyright (C) 2007 IBM
- *
- * Author: Darrick J. Wong <djwong@us.ibm.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/module.h>
-#include <linux/jiffies.h>
-#include <linux/i2c.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/log2.h>
-
-/* Addresses to scan */
-static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, I2C_CLIENT_END };
-
-/* ADT7473 registers */
-#define ADT7473_REG_BASE_ADDR                  0x20
-
-#define ADT7473_REG_VOLT_BASE_ADDR             0x21
-#define ADT7473_REG_VOLT_MIN_BASE_ADDR         0x46
-
-#define ADT7473_REG_TEMP_BASE_ADDR             0x25
-#define ADT7473_REG_TEMP_LIMITS_BASE_ADDR      0x4E
-#define ADT7473_REG_TEMP_TMIN_BASE_ADDR                0x67
-#define ADT7473_REG_TEMP_TMAX_BASE_ADDR                0x6A
-
-#define ADT7473_REG_FAN_BASE_ADDR              0x28
-#define ADT7473_REG_FAN_MIN_BASE_ADDR          0x54
-
-#define ADT7473_REG_PWM_BASE_ADDR              0x30
-#define        ADT7473_REG_PWM_MIN_BASE_ADDR           0x64
-#define ADT7473_REG_PWM_MAX_BASE_ADDR          0x38
-#define ADT7473_REG_PWM_BHVR_BASE_ADDR         0x5C
-#define                ADT7473_PWM_BHVR_MASK           0xE0
-#define                ADT7473_PWM_BHVR_SHIFT          5
-
-#define ADT7473_REG_CFG1                       0x40
-#define        ADT7473_CFG1_START              0x01
-#define                ADT7473_CFG1_READY              0x04
-#define ADT7473_REG_CFG2                       0x73
-#define ADT7473_REG_CFG3                       0x78
-#define ADT7473_REG_CFG4                       0x7D
-#define                ADT7473_CFG4_MAX_DUTY_AT_OVT    0x08
-#define ADT7473_REG_CFG5                       0x7C
-#define                ADT7473_CFG5_TEMP_TWOS          0x01
-#define                ADT7473_CFG5_TEMP_OFFSET        0x02
-
-#define ADT7473_REG_DEVICE                     0x3D
-#define        ADT7473_VENDOR                  0x41
-#define ADT7473_REG_VENDOR                     0x3E
-#define        ADT7473_DEVICE                  0x73
-#define ADT7473_REG_REVISION                   0x3F
-#define        ADT7473_REV_68                  0x68
-#define        ADT7473_REV_69                  0x69
-
-#define ADT7473_REG_ALARM1                     0x41
-#define                ADT7473_VCCP_ALARM              0x02
-#define                ADT7473_VCC_ALARM               0x04
-#define                ADT7473_R1T_ALARM               0x10
-#define                ADT7473_LT_ALARM                0x20
-#define                ADT7473_R2T_ALARM               0x40
-#define                ADT7473_OOL                     0x80
-#define ADT7473_REG_ALARM2                     0x42
-#define                ADT7473_OVT_ALARM               0x02
-#define                ADT7473_FAN1_ALARM              0x04
-#define                ADT7473_FAN2_ALARM              0x08
-#define                ADT7473_FAN3_ALARM              0x10
-#define                ADT7473_FAN4_ALARM              0x20
-#define                ADT7473_R1T_SHORT               0x40
-#define                ADT7473_R2T_SHORT               0x80
-
-#define ALARM2(x)      ((x) << 8)
-
-#define ADT7473_VOLT_COUNT     2
-#define ADT7473_REG_VOLT(x)    (ADT7473_REG_VOLT_BASE_ADDR + (x))
-#define ADT7473_REG_VOLT_MIN(x)        (ADT7473_REG_VOLT_MIN_BASE_ADDR + ((x) * 2))
-#define ADT7473_REG_VOLT_MAX(x)        (ADT7473_REG_VOLT_MIN_BASE_ADDR + \
-                               ((x) * 2) + 1)
-
-#define ADT7473_TEMP_COUNT     3
-#define ADT7473_REG_TEMP(x)    (ADT7473_REG_TEMP_BASE_ADDR + (x))
-#define ADT7473_REG_TEMP_MIN(x) (ADT7473_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2))
-#define ADT7473_REG_TEMP_MAX(x) (ADT7473_REG_TEMP_LIMITS_BASE_ADDR + \
-                               ((x) * 2) + 1)
-#define ADT7473_REG_TEMP_TMIN(x)       (ADT7473_REG_TEMP_TMIN_BASE_ADDR + (x))
-#define ADT7473_REG_TEMP_TMAX(x)       (ADT7473_REG_TEMP_TMAX_BASE_ADDR + (x))
-
-#define ADT7473_FAN_COUNT      4
-#define ADT7473_REG_FAN(x)     (ADT7473_REG_FAN_BASE_ADDR + ((x) * 2))
-#define ADT7473_REG_FAN_MIN(x) (ADT7473_REG_FAN_MIN_BASE_ADDR + ((x) * 2))
-
-#define ADT7473_PWM_COUNT      3
-#define ADT7473_REG_PWM(x)     (ADT7473_REG_PWM_BASE_ADDR + (x))
-#define ADT7473_REG_PWM_MAX(x) (ADT7473_REG_PWM_MAX_BASE_ADDR + (x))
-#define ADT7473_REG_PWM_MIN(x) (ADT7473_REG_PWM_MIN_BASE_ADDR + (x))
-#define ADT7473_REG_PWM_BHVR(x)        (ADT7473_REG_PWM_BHVR_BASE_ADDR + (x))
-
-/* How often do we reread sensors values? (In jiffies) */
-#define SENSOR_REFRESH_INTERVAL        (2 * HZ)
-
-/* How often do we reread sensor limit values? (In jiffies) */
-#define LIMIT_REFRESH_INTERVAL (60 * HZ)
-
-/* datasheet says to divide this number by the fan reading to get fan rpm */
-#define FAN_PERIOD_TO_RPM(x)   ((90000 * 60) / (x))
-#define FAN_RPM_TO_PERIOD      FAN_PERIOD_TO_RPM
-#define FAN_PERIOD_INVALID     65535
-#define FAN_DATA_VALID(x)      ((x) && (x) != FAN_PERIOD_INVALID)
-
-struct adt7473_data {
-       struct device           *hwmon_dev;
-       struct attribute_group  attrs;
-       struct mutex            lock;
-       char                    sensors_valid;
-       char                    limits_valid;
-       unsigned long           sensors_last_updated;   /* In jiffies */
-       unsigned long           limits_last_updated;    /* In jiffies */
-
-       u8                      volt[ADT7473_VOLT_COUNT];
-       s8                      volt_min[ADT7473_VOLT_COUNT];
-       s8                      volt_max[ADT7473_VOLT_COUNT];
-
-       s8                      temp[ADT7473_TEMP_COUNT];
-       s8                      temp_min[ADT7473_TEMP_COUNT];
-       s8                      temp_max[ADT7473_TEMP_COUNT];
-       s8                      temp_tmin[ADT7473_TEMP_COUNT];
-       /* This is called the !THERM limit in the datasheet */
-       s8                      temp_tmax[ADT7473_TEMP_COUNT];
-
-       u16                     fan[ADT7473_FAN_COUNT];
-       u16                     fan_min[ADT7473_FAN_COUNT];
-
-       u8                      pwm[ADT7473_PWM_COUNT];
-       u8                      pwm_max[ADT7473_PWM_COUNT];
-       u8                      pwm_min[ADT7473_PWM_COUNT];
-       u8                      pwm_behavior[ADT7473_PWM_COUNT];
-
-       u8                      temp_twos_complement;
-       u8                      temp_offset;
-
-       u16                     alarm;
-       u8                      max_duty_at_overheat;
-};
-
-static int adt7473_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id);
-static int adt7473_detect(struct i2c_client *client,
-                         struct i2c_board_info *info);
-static int adt7473_remove(struct i2c_client *client);
-
-static const struct i2c_device_id adt7473_id[] = {
-       { "adt7473", 0 },
-       { }
-};
-
-static struct i2c_driver adt7473_driver = {
-       .class          = I2C_CLASS_HWMON,
-       .driver = {
-               .name   = "adt7473",
-       },
-       .probe          = adt7473_probe,
-       .remove         = adt7473_remove,
-       .id_table       = adt7473_id,
-       .detect         = adt7473_detect,
-       .address_list   = normal_i2c,
-};
-
-/*
- * 16-bit registers on the ADT7473 are low-byte first.  The data sheet says
- * that the low byte must be read before the high byte.
- */
-static inline int adt7473_read_word_data(struct i2c_client *client, u8 reg)
-{
-       u16 foo;
-       foo = i2c_smbus_read_byte_data(client, reg);
-       foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8);
-       return foo;
-}
-
-static inline int adt7473_write_word_data(struct i2c_client *client, u8 reg,
-                                         u16 value)
-{
-       return i2c_smbus_write_byte_data(client, reg, value & 0xFF)
-              && i2c_smbus_write_byte_data(client, reg + 1, value >> 8);
-}
-
-static void adt7473_init_client(struct i2c_client *client)
-{
-       int reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG1);
-
-       if (!(reg & ADT7473_CFG1_READY)) {
-               dev_err(&client->dev, "Chip not ready.\n");
-       } else {
-               /* start monitoring */
-               i2c_smbus_write_byte_data(client, ADT7473_REG_CFG1,
-                                         reg | ADT7473_CFG1_START);
-       }
-}
-
-static struct adt7473_data *adt7473_update_device(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       unsigned long local_jiffies = jiffies;
-       u8 cfg;
-       int i;
-
-       mutex_lock(&data->lock);
-       if (time_before(local_jiffies, data->sensors_last_updated +
-               SENSOR_REFRESH_INTERVAL)
-               && data->sensors_valid)
-               goto no_sensor_update;
-
-       for (i = 0; i < ADT7473_VOLT_COUNT; i++)
-               data->volt[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_VOLT(i));
-
-       /* Determine temperature encoding */
-       cfg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG5);
-       data->temp_twos_complement = (cfg & ADT7473_CFG5_TEMP_TWOS);
-
-       /*
-        * What does this do? it implies a variable temperature sensor
-        * offset, but the datasheet doesn't say anything about this bit
-        * and other parts of the datasheet imply that "offset64" mode
-        * means that you shift temp values by -64 if the above bit was set.
-        */
-       data->temp_offset = (cfg & ADT7473_CFG5_TEMP_OFFSET);
-
-       for (i = 0; i < ADT7473_TEMP_COUNT; i++)
-               data->temp[i] = i2c_smbus_read_byte_data(client,
-                                                        ADT7473_REG_TEMP(i));
-
-       for (i = 0; i < ADT7473_FAN_COUNT; i++)
-               data->fan[i] = adt7473_read_word_data(client,
-                                               ADT7473_REG_FAN(i));
-
-       for (i = 0; i < ADT7473_PWM_COUNT; i++)
-               data->pwm[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_PWM(i));
-
-       data->alarm = i2c_smbus_read_byte_data(client, ADT7473_REG_ALARM1);
-       if (data->alarm & ADT7473_OOL)
-               data->alarm |= ALARM2(i2c_smbus_read_byte_data(client,
-                                                        ADT7473_REG_ALARM2));
-
-       data->sensors_last_updated = local_jiffies;
-       data->sensors_valid = 1;
-
-no_sensor_update:
-       if (time_before(local_jiffies, data->limits_last_updated +
-               LIMIT_REFRESH_INTERVAL)
-               && data->limits_valid)
-               goto out;
-
-       for (i = 0; i < ADT7473_VOLT_COUNT; i++) {
-               data->volt_min[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_VOLT_MIN(i));
-               data->volt_max[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_VOLT_MAX(i));
-       }
-
-       for (i = 0; i < ADT7473_TEMP_COUNT; i++) {
-               data->temp_min[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_TEMP_MIN(i));
-               data->temp_max[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_TEMP_MAX(i));
-               data->temp_tmin[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_TEMP_TMIN(i));
-               data->temp_tmax[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_TEMP_TMAX(i));
-       }
-
-       for (i = 0; i < ADT7473_FAN_COUNT; i++)
-               data->fan_min[i] = adt7473_read_word_data(client,
-                                               ADT7473_REG_FAN_MIN(i));
-
-       for (i = 0; i < ADT7473_PWM_COUNT; i++) {
-               data->pwm_max[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_PWM_MAX(i));
-               data->pwm_min[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_PWM_MIN(i));
-               data->pwm_behavior[i] = i2c_smbus_read_byte_data(client,
-                                               ADT7473_REG_PWM_BHVR(i));
-       }
-
-       i = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4);
-       data->max_duty_at_overheat = !!(i & ADT7473_CFG4_MAX_DUTY_AT_OVT);
-
-       data->limits_last_updated = local_jiffies;
-       data->limits_valid = 1;
-
-out:
-       mutex_unlock(&data->lock);
-       return data;
-}
-
-/*
- * Conversions
- */
-
-/* IN are scaled acording to built-in resistors */
-static const int adt7473_scaling[] = {  /* .001 Volts */
-       2250, 3300
-};
-#define SCALE(val, from, to)   (((val) * (to) + ((from) / 2)) / (from))
-
-static int decode_volt(int volt_index, u8 raw)
-{
-       return SCALE(raw, 192, adt7473_scaling[volt_index]);
-}
-
-static u8 encode_volt(int volt_index, int cooked)
-{
-       int raw = SCALE(cooked, adt7473_scaling[volt_index], 192);
-       return SENSORS_LIMIT(raw, 0, 255);
-}
-
-static ssize_t show_volt_min(struct device *dev,
-                            struct device_attribute *devattr,
-                            char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n",
-                      decode_volt(attr->index, data->volt_min[attr->index]));
-}
-
-static ssize_t set_volt_min(struct device *dev,
-                           struct device_attribute *devattr,
-                           const char *buf,
-                           size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long volt;
-
-       if (strict_strtol(buf, 10, &volt))
-               return -EINVAL;
-
-       volt = encode_volt(attr->index, volt);
-
-       mutex_lock(&data->lock);
-       data->volt_min[attr->index] = volt;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_VOLT_MIN(attr->index),
-                                 volt);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_volt_max(struct device *dev,
-                            struct device_attribute *devattr,
-                            char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n",
-                      decode_volt(attr->index, data->volt_max[attr->index]));
-}
-
-static ssize_t set_volt_max(struct device *dev,
-                           struct device_attribute *devattr,
-                           const char *buf,
-                           size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long volt;
-
-       if (strict_strtol(buf, 10, &volt))
-               return -EINVAL;
-
-       volt = encode_volt(attr->index, volt);
-
-       mutex_lock(&data->lock);
-       data->volt_max[attr->index] = volt;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_VOLT_MAX(attr->index),
-                                 volt);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_volt(struct device *dev, struct device_attribute *devattr,
-                        char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-
-       return sprintf(buf, "%d\n",
-                      decode_volt(attr->index, data->volt[attr->index]));
-}
-
-/*
- * This chip can report temperature data either as a two's complement
- * number in the range -128 to 127, or as an unsigned number that must
- * be offset by 64.
- */
-static int decode_temp(u8 twos_complement, u8 raw)
-{
-       return twos_complement ? (s8)raw : raw - 64;
-}
-
-static u8 encode_temp(u8 twos_complement, int cooked)
-{
-       u8 ret = twos_complement ? cooked & 0xFF : cooked + 64;
-       return SENSORS_LIMIT(ret, 0, 255);
-}
-
-static ssize_t show_temp_min(struct device *dev,
-                            struct device_attribute *devattr,
-                            char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", 1000 * decode_temp(
-                                               data->temp_twos_complement,
-                                               data->temp_min[attr->index]));
-}
-
-static ssize_t set_temp_min(struct device *dev,
-                           struct device_attribute *devattr,
-                           const char *buf,
-                           size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = DIV_ROUND_CLOSEST(temp, 1000);
-       temp = encode_temp(data->temp_twos_complement, temp);
-
-       mutex_lock(&data->lock);
-       data->temp_min[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_MIN(attr->index),
-                                 temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_temp_max(struct device *dev,
-                            struct device_attribute *devattr,
-                            char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", 1000 * decode_temp(
-                                               data->temp_twos_complement,
-                                               data->temp_max[attr->index]));
-}
-
-static ssize_t set_temp_max(struct device *dev,
-                           struct device_attribute *devattr,
-                           const char *buf,
-                           size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = DIV_ROUND_CLOSEST(temp, 1000);
-       temp = encode_temp(data->temp_twos_complement, temp);
-
-       mutex_lock(&data->lock);
-       data->temp_max[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_MAX(attr->index),
-                                 temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
-                        char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", 1000 * decode_temp(
-                                               data->temp_twos_complement,
-                                               data->temp[attr->index]));
-}
-
-static ssize_t show_fan_min(struct device *dev,
-                           struct device_attribute *devattr,
-                           char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-
-       if (FAN_DATA_VALID(data->fan_min[attr->index]))
-               return sprintf(buf, "%d\n",
-                              FAN_PERIOD_TO_RPM(data->fan_min[attr->index]));
-       else
-               return sprintf(buf, "0\n");
-}
-
-static ssize_t set_fan_min(struct device *dev,
-                          struct device_attribute *devattr,
-                          const char *buf, size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp) || !temp)
-               return -EINVAL;
-
-       temp = FAN_RPM_TO_PERIOD(temp);
-       temp = SENSORS_LIMIT(temp, 1, 65534);
-
-       mutex_lock(&data->lock);
-       data->fan_min[attr->index] = temp;
-       adt7473_write_word_data(client, ADT7473_REG_FAN_MIN(attr->index), temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
-                       char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-
-       if (FAN_DATA_VALID(data->fan[attr->index]))
-               return sprintf(buf, "%d\n",
-                              FAN_PERIOD_TO_RPM(data->fan[attr->index]));
-       else
-               return sprintf(buf, "0\n");
-}
-
-static ssize_t show_max_duty_at_crit(struct device *dev,
-                                    struct device_attribute *devattr,
-                                    char *buf)
-{
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", data->max_duty_at_overheat);
-}
-
-static ssize_t set_max_duty_at_crit(struct device *dev,
-                                   struct device_attribute *devattr,
-                                   const char *buf,
-                                   size_t count)
-{
-       u8 reg;
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       mutex_lock(&data->lock);
-       data->max_duty_at_overheat = !!temp;
-       reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4);
-       if (temp)
-               reg |= ADT7473_CFG4_MAX_DUTY_AT_OVT;
-       else
-               reg &= ~ADT7473_CFG4_MAX_DUTY_AT_OVT;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_CFG4, reg);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
-                       char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", data->pwm[attr->index]);
-}
-
-static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
-                       const char *buf, size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = SENSORS_LIMIT(temp, 0, 255);
-
-       mutex_lock(&data->lock);
-       data->pwm[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_PWM(attr->index), temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_pwm_max(struct device *dev,
-                           struct device_attribute *devattr,
-                           char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", data->pwm_max[attr->index]);
-}
-
-static ssize_t set_pwm_max(struct device *dev,
-                          struct device_attribute *devattr,
-                          const char *buf,
-                          size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = SENSORS_LIMIT(temp, 0, 255);
-
-       mutex_lock(&data->lock);
-       data->pwm_max[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_MAX(attr->index),
-                                 temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_pwm_min(struct device *dev,
-                           struct device_attribute *devattr,
-                           char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", data->pwm_min[attr->index]);
-}
-
-static ssize_t set_pwm_min(struct device *dev,
-                          struct device_attribute *devattr,
-                          const char *buf,
-                          size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = SENSORS_LIMIT(temp, 0, 255);
-
-       mutex_lock(&data->lock);
-       data->pwm_min[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_MIN(attr->index),
-                                 temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_temp_tmax(struct device *dev,
-                             struct device_attribute *devattr,
-                             char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", 1000 * decode_temp(
-                                               data->temp_twos_complement,
-                                               data->temp_tmax[attr->index]));
-}
-
-static ssize_t set_temp_tmax(struct device *dev,
-                            struct device_attribute *devattr,
-                            const char *buf,
-                            size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = DIV_ROUND_CLOSEST(temp, 1000);
-       temp = encode_temp(data->temp_twos_complement, temp);
-
-       mutex_lock(&data->lock);
-       data->temp_tmax[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_TMAX(attr->index),
-                                 temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_temp_tmin(struct device *dev,
-                             struct device_attribute *devattr,
-                             char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       return sprintf(buf, "%d\n", 1000 * decode_temp(
-                                               data->temp_twos_complement,
-                                               data->temp_tmin[attr->index]));
-}
-
-static ssize_t set_temp_tmin(struct device *dev,
-                            struct device_attribute *devattr,
-                            const char *buf,
-                            size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       temp = DIV_ROUND_CLOSEST(temp, 1000);
-       temp = encode_temp(data->temp_twos_complement, temp);
-
-       mutex_lock(&data->lock);
-       data->temp_tmin[attr->index] = temp;
-       i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_TMIN(attr->index),
-                                 temp);
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_pwm_enable(struct device *dev,
-                              struct device_attribute *devattr,
-                              char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-
-       switch (data->pwm_behavior[attr->index] >> ADT7473_PWM_BHVR_SHIFT) {
-       case 3:
-               return sprintf(buf, "0\n");
-       case 7:
-               return sprintf(buf, "1\n");
-       default:
-               return sprintf(buf, "2\n");
-       }
-}
-
-static ssize_t set_pwm_enable(struct device *dev,
-                             struct device_attribute *devattr,
-                             const char *buf,
-                             size_t count)
-{
-       u8 reg;
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       switch (temp) {
-       case 0:
-               temp = 3;
-               break;
-       case 1:
-               temp = 7;
-               break;
-       case 2:
-               /* Enter automatic mode with fans off */
-               temp = 4;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       mutex_lock(&data->lock);
-       reg = i2c_smbus_read_byte_data(client,
-                                      ADT7473_REG_PWM_BHVR(attr->index));
-       reg = (temp << ADT7473_PWM_BHVR_SHIFT) |
-             (reg & ~ADT7473_PWM_BHVR_MASK);
-       i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_BHVR(attr->index),
-                                 reg);
-       data->pwm_behavior[attr->index] = reg;
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_pwm_auto_temp(struct device *dev,
-                                 struct device_attribute *devattr,
-                                 char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-       int bhvr = data->pwm_behavior[attr->index] >> ADT7473_PWM_BHVR_SHIFT;
-
-       switch (bhvr) {
-       case 3:
-       case 4:
-       case 7:
-               return sprintf(buf, "0\n");
-       case 0:
-       case 1:
-       case 5:
-       case 6:
-               return sprintf(buf, "%d\n", bhvr + 1);
-       case 2:
-               return sprintf(buf, "4\n");
-       }
-       /* shouldn't ever get here */
-       BUG();
-}
-
-static ssize_t set_pwm_auto_temp(struct device *dev,
-                                struct device_attribute *devattr,
-                                const char *buf,
-                                size_t count)
-{
-       u8 reg;
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7473_data *data = i2c_get_clientdata(client);
-       long temp;
-
-       if (strict_strtol(buf, 10, &temp))
-               return -EINVAL;
-
-       switch (temp) {
-       case 1:
-       case 2:
-       case 6:
-       case 7:
-               temp--;
-               break;
-       case 0:
-               temp = 4;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       mutex_lock(&data->lock);
-       reg = i2c_smbus_read_byte_data(client,
-                                      ADT7473_REG_PWM_BHVR(attr->index));
-       reg = (temp << ADT7473_PWM_BHVR_SHIFT) |
-             (reg & ~ADT7473_PWM_BHVR_MASK);
-       i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_BHVR(attr->index),
-                                 reg);
-       data->pwm_behavior[attr->index] = reg;
-       mutex_unlock(&data->lock);
-
-       return count;
-}
-
-static ssize_t show_alarm(struct device *dev,
-                         struct device_attribute *devattr,
-                         char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adt7473_data *data = adt7473_update_device(dev);
-
-       if (data->alarm & attr->index)
-               return sprintf(buf, "1\n");
-       else
-               return sprintf(buf, "0\n");
-}
-
-
-static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_volt_max,
-                         set_volt_max, 0);
-static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_volt_max,
-                         set_volt_max, 1);
-
-static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_volt_min,
-                         set_volt_min, 0);
-static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_volt_min,
-                         set_volt_min, 1);
-
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_volt, NULL, 0);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_volt, NULL, 1);
-
-static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL,
-                         ADT7473_VCCP_ALARM);
-static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL,
-                         ADT7473_VCC_ALARM);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
-                         set_temp_max, 0);
-static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
-                         set_temp_max, 1);
-static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max,
-                         set_temp_max, 2);
-
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
-                         set_temp_min, 0);
-static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
-                         set_temp_min, 1);
-static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min,
-                         set_temp_min, 2);
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
-
-static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL,
-                         ADT7473_R1T_ALARM | ALARM2(ADT7473_R1T_SHORT));
-static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL,
-                         ADT7473_LT_ALARM);
-static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL,
-                         ADT7473_R2T_ALARM | ALARM2(ADT7473_R2T_SHORT));
-
-static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
-                         set_fan_min, 0);
-static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
-                         set_fan_min, 1);
-static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
-                         set_fan_min, 2);
-static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
-                         set_fan_min, 3);
-
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL,
-                         ALARM2(ADT7473_FAN1_ALARM));
-static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL,
-                         ALARM2(ADT7473_FAN2_ALARM));
-static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL,
-                         ALARM2(ADT7473_FAN3_ALARM));
-static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL,
-                         ALARM2(ADT7473_FAN4_ALARM));
-
-static SENSOR_DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO,
-                         show_max_duty_at_crit, set_max_duty_at_crit, 0);
-
-static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
-static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
-static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
-
-static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
-                         show_pwm_min, set_pwm_min, 0);
-static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
-                         show_pwm_min, set_pwm_min, 1);
-static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO,
-                         show_pwm_min, set_pwm_min, 2);
-
-static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
-                         show_pwm_max, set_pwm_max, 0);
-static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO,
-                         show_pwm_max, set_pwm_max, 1);
-static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO,
-                         show_pwm_max, set_pwm_max, 2);
-
-static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IWUSR | S_IRUGO,
-                         show_temp_tmin, set_temp_tmin, 0);
-static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO,
-                         show_temp_tmin, set_temp_tmin, 1);
-static SENSOR_DEVICE_ATTR(temp3_auto_point1_temp, S_IWUSR | S_IRUGO,
-                         show_temp_tmin, set_temp_tmin, 2);
-
-static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO,
-                         show_temp_tmax, set_temp_tmax, 0);
-static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO,
-                         show_temp_tmax, set_temp_tmax, 1);
-static SENSOR_DEVICE_ATTR(temp3_auto_point2_temp, S_IWUSR | S_IRUGO,
-                         show_temp_tmax, set_temp_tmax, 2);
-
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
-                         set_pwm_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
-                         set_pwm_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
-                         set_pwm_enable, 2);
-
-static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO,
-                         show_pwm_auto_temp, set_pwm_auto_temp, 0);
-static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO,
-                         show_pwm_auto_temp, set_pwm_auto_temp, 1);
-static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO,
-                         show_pwm_auto_temp, set_pwm_auto_temp, 2);
-
-static struct attribute *adt7473_attr[] =
-{
-       &sensor_dev_attr_in1_max.dev_attr.attr,
-       &sensor_dev_attr_in2_max.dev_attr.attr,
-       &sensor_dev_attr_in1_min.dev_attr.attr,
-       &sensor_dev_attr_in2_min.dev_attr.attr,
-       &sensor_dev_attr_in1_input.dev_attr.attr,
-       &sensor_dev_attr_in2_input.dev_attr.attr,
-       &sensor_dev_attr_in1_alarm.dev_attr.attr,
-       &sensor_dev_attr_in2_alarm.dev_attr.attr,
-
-       &sensor_dev_attr_temp1_max.dev_attr.attr,
-       &sensor_dev_attr_temp2_max.dev_attr.attr,
-       &sensor_dev_attr_temp3_max.dev_attr.attr,
-       &sensor_dev_attr_temp1_min.dev_attr.attr,
-       &sensor_dev_attr_temp2_min.dev_attr.attr,
-       &sensor_dev_attr_temp3_min.dev_attr.attr,
-       &sensor_dev_attr_temp1_input.dev_attr.attr,
-       &sensor_dev_attr_temp2_input.dev_attr.attr,
-       &sensor_dev_attr_temp3_input.dev_attr.attr,
-       &sensor_dev_attr_temp1_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp2_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp3_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
-       &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
-       &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr,
-       &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
-       &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
-       &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
-
-       &sensor_dev_attr_fan1_min.dev_attr.attr,
-       &sensor_dev_attr_fan2_min.dev_attr.attr,
-       &sensor_dev_attr_fan3_min.dev_attr.attr,
-       &sensor_dev_attr_fan4_min.dev_attr.attr,
-       &sensor_dev_attr_fan1_input.dev_attr.attr,
-       &sensor_dev_attr_fan2_input.dev_attr.attr,
-       &sensor_dev_attr_fan3_input.dev_attr.attr,
-       &sensor_dev_attr_fan4_input.dev_attr.attr,
-       &sensor_dev_attr_fan1_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan2_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan3_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan4_alarm.dev_attr.attr,
-
-       &sensor_dev_attr_pwm_use_point2_pwm_at_crit.dev_attr.attr,
-
-       &sensor_dev_attr_pwm1.dev_attr.attr,
-       &sensor_dev_attr_pwm2.dev_attr.attr,
-       &sensor_dev_attr_pwm3.dev_attr.attr,
-       &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
-
-       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
-       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
-       &sensor_dev_attr_pwm3_enable.dev_attr.attr,
-       &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
-       &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
-       &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
-
-       NULL
-};
-
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int adt7473_detect(struct i2c_client *client,
-                         struct i2c_board_info *info)
-{
-       struct i2c_adapter *adapter = client->adapter;
-       int vendor, device, revision;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-               return -ENODEV;
-
-       vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR);
-       if (vendor != ADT7473_VENDOR)
-               return -ENODEV;
-
-       device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE);
-       if (device != ADT7473_DEVICE)
-               return -ENODEV;
-
-       revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION);
-       if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69)
-               return -ENODEV;
-
-       strlcpy(info->type, "adt7473", I2C_NAME_SIZE);
-
-       return 0;
-}
-
-static int adt7473_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
-{
-       struct adt7473_data *data;
-       int err;
-
-       data = kzalloc(sizeof(struct adt7473_data), GFP_KERNEL);
-       if (!data) {
-               err = -ENOMEM;
-               goto exit;
-       }
-
-       i2c_set_clientdata(client, data);
-       mutex_init(&data->lock);
-
-       dev_info(&client->dev, "%s chip found\n", client->name);
-
-       /* Initialize the ADT7473 chip */
-       adt7473_init_client(client);
-
-       /* Register sysfs hooks */
-       data->attrs.attrs = adt7473_attr;
-       err = sysfs_create_group(&client->dev.kobj, &data->attrs);
-       if (err)
-               goto exit_free;
-
-       data->hwmon_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               err = PTR_ERR(data->hwmon_dev);
-               goto exit_remove;
-       }
-
-       return 0;
-
-exit_remove:
-       sysfs_remove_group(&client->dev.kobj, &data->attrs);
-exit_free:
-       kfree(data);
-exit:
-       return err;
-}
-
-static int adt7473_remove(struct i2c_client *client)
-{
-       struct adt7473_data *data = i2c_get_clientdata(client);
-
-       hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&client->dev.kobj, &data->attrs);
-       kfree(data);
-       return 0;
-}
-
-static int __init adt7473_init(void)
-{
-       pr_notice("The adt7473 driver is deprecated, please use the adt7475 "
-                 "driver instead\n");
-       return i2c_add_driver(&adt7473_driver);
-}
-
-static void __exit adt7473_exit(void)
-{
-       i2c_del_driver(&adt7473_driver);
-}
-
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
-MODULE_DESCRIPTION("ADT7473 driver");
-MODULE_LICENSE("GPL");
-
-module_init(adt7473_init);
-module_exit(adt7473_exit);
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
new file mode 100644 (file)
index 0000000..7f94810
--- /dev/null
@@ -0,0 +1,1255 @@
+/*
+ * asc7621.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ * Copyright (c) 2007, 2010 George Joseph  <george.joseph@fairview5.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = {
+       0x2c, 0x2d, 0x2e, I2C_CLIENT_END
+};
+
+enum asc7621_type {
+       asc7621,
+       asc7621a
+};
+
+#define INTERVAL_HIGH   (HZ + HZ / 2)
+#define INTERVAL_LOW    (1 * 60 * HZ)
+#define PRI_NONE        0
+#define PRI_LOW         1
+#define PRI_HIGH        2
+#define FIRST_CHIP      asc7621
+#define LAST_CHIP       asc7621a
+
+struct asc7621_chip {
+       char *name;
+       enum asc7621_type chip_type;
+       u8 company_reg;
+       u8 company_id;
+       u8 verstep_reg;
+       u8 verstep_id;
+       unsigned short *addresses;
+};
+
+static struct asc7621_chip asc7621_chips[] = {
+       {
+               .name = "asc7621",
+               .chip_type = asc7621,
+               .company_reg = 0x3e,
+               .company_id = 0x61,
+               .verstep_reg = 0x3f,
+               .verstep_id = 0x6c,
+               .addresses = normal_i2c,
+        },
+       {
+               .name = "asc7621a",
+               .chip_type = asc7621a,
+               .company_reg = 0x3e,
+               .company_id = 0x61,
+               .verstep_reg = 0x3f,
+               .verstep_id = 0x6d,
+               .addresses = normal_i2c,
+        },
+};
+
+/*
+ * Defines the highest register to be used, not the count.
+ * The actual count will probably be smaller because of gaps
+ * in the implementation (unused register locations).
+ * This define will safely set the array size of both the parameter
+ * and data arrays.
+ * This comes from the data sheet register description table.
+ */
+#define LAST_REGISTER 0xff
+
+struct asc7621_data {
+       struct i2c_client client;
+       struct device *class_dev;
+       struct mutex update_lock;
+       int valid;              /* !=0 if following fields are valid */
+       unsigned long last_high_reading;        /* In jiffies */
+       unsigned long last_low_reading;         /* In jiffies */
+       /*
+        * Registers we care about occupy the corresponding index
+        * in the array.  Registers we don't care about are left
+        * at 0.
+        */
+       u8 reg[LAST_REGISTER + 1];
+};
+
+/*
+ * Macro to get the parent asc7621_param structure
+ * from a sensor_device_attribute passed into the
+ * show/store functions.
+ */
+#define to_asc7621_param(_sda) \
+       container_of(_sda, struct asc7621_param, sda)
+
+/*
+ * Each parameter to be retrieved needs an asc7621_param structure
+ * allocated.  It contains the sensor_device_attribute structure
+ * and the control info needed to retrieve the value from the register map.
+ */
+struct asc7621_param {
+       struct sensor_device_attribute sda;
+       u8 priority;
+       u8 msb[3];
+       u8 lsb[3];
+       u8 mask[3];
+       u8 shift[3];
+};
+
+/*
+ * This is the map that ultimately indicates whether we'll be
+ * retrieving a register value or not, and at what frequency.
+ */
+static u8 asc7621_register_priorities[255];
+
+static struct asc7621_data *asc7621_update_device(struct device *dev);
+
+static inline u8 read_byte(struct i2c_client *client, u8 reg)
+{
+       int res = i2c_smbus_read_byte_data(client, reg);
+       if (res < 0) {
+               dev_err(&client->dev,
+                       "Unable to read from register 0x%02x.\n", reg);
+               return 0;
+       };
+       return res & 0xff;
+}
+
+static inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
+{
+       int res = i2c_smbus_write_byte_data(client, reg, data);
+       if (res < 0) {
+               dev_err(&client->dev,
+                       "Unable to write value 0x%02x to register 0x%02x.\n",
+                       data, reg);
+       };
+       return res;
+}
+
+/*
+ * Data Handlers
+ * Each function handles the formatting, storage
+ * and retrieval of like parameters.
+ */
+
+#define SETUP_SHOW_data_param(d, a) \
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
+       struct asc7621_data *data = asc7621_update_device(d); \
+       struct asc7621_param *param = to_asc7621_param(sda)
+
+#define SETUP_STORE_data_param(d, a) \
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
+       struct i2c_client *client = to_i2c_client(d); \
+       struct asc7621_data *data = i2c_get_clientdata(client); \
+       struct asc7621_param *param = to_asc7621_param(sda)
+
+/*
+ * u8 is just what it sounds like...an unsigned byte with no
+ * special formatting.
+ */
+static ssize_t show_u8(struct device *dev, struct device_attribute *attr,
+                      char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+
+       return sprintf(buf, "%u\n", data->reg[param->msb[0]]);
+}
+
+static ssize_t store_u8(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       reqval = SENSORS_LIMIT(reqval, 0, 255);
+
+       mutex_lock(&data->update_lock);
+       data->reg[param->msb[0]] = reqval;
+       write_byte(client, param->msb[0], reqval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * Many of the config values occupy only a few bits of a register.
+ */
+static ssize_t show_bitmask(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+
+       return sprintf(buf, "%u\n",
+                      (data->reg[param->msb[0]] >> param->
+                       shift[0]) & param->mask[0]);
+}
+
+static ssize_t store_bitmask(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+       u8 currval;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       reqval = SENSORS_LIMIT(reqval, 0, param->mask[0]);
+
+       reqval = (reqval & param->mask[0]) << param->shift[0];
+
+       mutex_lock(&data->update_lock);
+       currval = read_byte(client, param->msb[0]);
+       reqval |= (currval & ~(param->mask[0] << param->shift[0]));
+       data->reg[param->msb[0]] = reqval;
+       write_byte(client, param->msb[0], reqval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * 16 bit fan rpm values
+ * reported by the device as the number of 11.111us periods (90khz)
+ * between full fan rotations.  Therefore...
+ * RPM = (90000 * 60) / register value
+ */
+static ssize_t show_fan16(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u16 regval;
+
+       mutex_lock(&data->update_lock);
+       regval = (data->reg[param->msb[0]] << 8) | data->reg[param->lsb[0]];
+       mutex_unlock(&data->update_lock);
+
+       return sprintf(buf, "%u\n",
+                      (regval == 0 ? -1 : (regval) ==
+                       0xffff ? 0 : 5400000 / regval));
+}
+
+static ssize_t store_fan16(struct device *dev,
+                          struct device_attribute *attr, const char *buf,
+                          size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       reqval =
+           (SENSORS_LIMIT((reqval) <= 0 ? 0 : 5400000 / (reqval), 0, 65534));
+
+       mutex_lock(&data->update_lock);
+       data->reg[param->msb[0]] = (reqval >> 8) & 0xff;
+       data->reg[param->lsb[0]] = reqval & 0xff;
+       write_byte(client, param->msb[0], data->reg[param->msb[0]]);
+       write_byte(client, param->lsb[0], data->reg[param->lsb[0]]);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+/*
+ * Voltages are scaled in the device so that the nominal voltage
+ * is 3/4ths of the 0-255 range (i.e. 192).
+ * If all voltages are 'normal' then all voltage registers will
+ * read 0xC0.  This doesn't help us if we don't have a point of refernce.
+ * The data sheet however provides us with the full scale value for each
+ * which is stored in in_scaling.  The sda->index parameter value provides
+ * the index into in_scaling.
+ *
+ * NOTE: The chip expects the first 2 inputs be 2.5 and 2.25 volts
+ * respectively. That doesn't mean that's what the motherboard provides. :)
+ */
+
+static int asc7621_in_scaling[] = {
+       3320, 3000, 4380, 6640, 16000
+};
+
+static ssize_t show_in10(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u16 regval;
+       u8 nr = sda->index;
+
+       mutex_lock(&data->update_lock);
+       regval = (data->reg[param->msb[0]] * asc7621_in_scaling[nr]) / 256;
+
+       /* The LSB value is a 2-bit scaling of the MSB's LSbit value.
+        * I.E.  If the maximim voltage for this input is 6640 millivolts then
+        * a MSB register value of 0 = 0mv and 255 = 6640mv.
+        * A 1 step change therefore represents 25.9mv (6640 / 256).
+        * The extra 2-bits therefore represent increments of 6.48mv.
+        */
+       regval += ((asc7621_in_scaling[nr] / 256) / 4) *
+           (data->reg[param->lsb[0]] >> 6);
+
+       mutex_unlock(&data->update_lock);
+
+       return sprintf(buf, "%u\n", regval);
+}
+
+/* 8 bit voltage values (the mins and maxs) */
+static ssize_t show_in8(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 nr = sda->index;
+
+       return sprintf(buf, "%u\n",
+                      ((data->reg[param->msb[0]] *
+                        asc7621_in_scaling[nr]) / 256));
+}
+
+static ssize_t store_in8(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+       u8 nr = sda->index;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       reqval = SENSORS_LIMIT(reqval, 0, asc7621_in_scaling[nr]);
+
+       reqval = (reqval * 255 + 128) / asc7621_in_scaling[nr];
+
+       mutex_lock(&data->update_lock);
+       data->reg[param->msb[0]] = reqval;
+       write_byte(client, param->msb[0], reqval);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_temp8(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+
+       return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000);
+}
+
+static ssize_t store_temp8(struct device *dev,
+                          struct device_attribute *attr, const char *buf,
+                          size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+       s8 temp;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       reqval = SENSORS_LIMIT(reqval, -127000, 127000);
+
+       temp = reqval / 1000;
+
+       mutex_lock(&data->update_lock);
+       data->reg[param->msb[0]] = temp;
+       write_byte(client, param->msb[0], temp);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * Temperatures that occupy 2 bytes always have the whole
+ * number of degrees in the MSB with some part of the LSB
+ * indicating fractional degrees.
+ */
+
+/*   mmmmmmmm.llxxxxxx */
+static ssize_t show_temp10(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 msb, lsb;
+       int temp;
+
+       mutex_lock(&data->update_lock);
+       msb = data->reg[param->msb[0]];
+       lsb = (data->reg[param->lsb[0]] >> 6) & 0x03;
+       temp = (((s8) msb) * 1000) + (lsb * 250);
+       mutex_unlock(&data->update_lock);
+
+       return sprintf(buf, "%d\n", temp);
+}
+
+/*   mmmmmm.ll */
+static ssize_t show_temp62(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 regval = data->reg[param->msb[0]];
+       int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250);
+
+       return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t store_temp62(struct device *dev,
+                           struct device_attribute *attr, const char *buf,
+                           size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval, i, f;
+       s8 temp;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       reqval = SENSORS_LIMIT(reqval, -32000, 31750);
+       i = reqval / 1000;
+       f = reqval - (i * 1000);
+       temp = i << 2;
+       temp |= f / 250;
+
+       mutex_lock(&data->update_lock);
+       data->reg[param->msb[0]] = temp;
+       write_byte(client, param->msb[0], temp);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * The aSC7621 doesn't provide an "auto_point2".  Instead, you
+ * specify the auto_point1 and a range.  To keep with the sysfs
+ * hwmon specs, we synthesize the auto_point_2 from them.
+ */
+
+static u32 asc7621_range_map[] = {
+       2000, 2500, 3330, 4000, 5000, 6670, 8000, 10000,
+       13330, 16000, 20000, 26670, 32000, 40000, 53330, 80000,
+};
+
+static ssize_t show_ap2_temp(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       long auto_point1;
+       u8 regval;
+       int temp;
+
+       mutex_lock(&data->update_lock);
+       auto_point1 = ((s8) data->reg[param->msb[1]]) * 1000;
+       regval =
+           ((data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]);
+       temp = auto_point1 + asc7621_range_map[SENSORS_LIMIT(regval, 0, 15)];
+       mutex_unlock(&data->update_lock);
+
+       return sprintf(buf, "%d\n", temp);
+
+}
+
+static ssize_t store_ap2_temp(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval, auto_point1;
+       int i;
+       u8 currval, newval = 0;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       auto_point1 = data->reg[param->msb[1]] * 1000;
+       reqval = SENSORS_LIMIT(reqval, auto_point1 + 2000, auto_point1 + 80000);
+
+       for (i = ARRAY_SIZE(asc7621_range_map) - 1; i >= 0; i--) {
+               if (reqval >= auto_point1 + asc7621_range_map[i]) {
+                       newval = i;
+                       break;
+               }
+       }
+
+       newval = (newval & param->mask[0]) << param->shift[0];
+       currval = read_byte(client, param->msb[0]);
+       newval |= (currval & ~(param->mask[0] << param->shift[0]));
+       data->reg[param->msb[0]] = newval;
+       write_byte(client, param->msb[0], newval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_pwm_ac(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 config, altbit, regval;
+       u8 map[] = {
+               0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
+               0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f
+       };
+
+       mutex_lock(&data->update_lock);
+       config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+       altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1];
+       regval = config | (altbit << 3);
+       mutex_unlock(&data->update_lock);
+
+       return sprintf(buf, "%u\n", map[SENSORS_LIMIT(regval, 0, 15)]);
+}
+
+static ssize_t store_pwm_ac(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       unsigned long reqval;
+       u8 currval, config, altbit, newval;
+       u16 map[] = {
+               0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06,
+               0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+               0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
+       };
+
+       if (strict_strtoul(buf, 10, &reqval))
+               return -EINVAL;
+
+       if (reqval > 31)
+               return -EINVAL;
+
+       reqval = map[reqval];
+       if (reqval == 0xff)
+               return -EINVAL;
+
+       config = reqval & 0x07;
+       altbit = (reqval >> 3) & 0x01;
+
+       config = (config & param->mask[0]) << param->shift[0];
+       altbit = (altbit & param->mask[1]) << param->shift[1];
+
+       mutex_lock(&data->update_lock);
+       currval = read_byte(client, param->msb[0]);
+       newval = config | (currval & ~(param->mask[0] << param->shift[0]));
+       newval = altbit | (newval & ~(param->mask[1] << param->shift[1]));
+       data->reg[param->msb[0]] = newval;
+       write_byte(client, param->msb[0], newval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_pwm_enable(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 config, altbit, minoff, val, newval;
+
+       mutex_lock(&data->update_lock);
+       config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+       altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1];
+       minoff = (data->reg[param->msb[2]] >> param->shift[2]) & param->mask[2];
+       mutex_unlock(&data->update_lock);
+
+       val = config | (altbit << 3);
+       newval = 0;
+
+       if (val == 3 || val >= 10)
+               newval = 255;
+       else if (val == 4)
+               newval = 0;
+       else if (val == 7)
+               newval = 1;
+       else if (minoff == 1)
+               newval = 2;
+       else
+               newval = 3;
+
+       return sprintf(buf, "%u\n", newval);
+}
+
+static ssize_t store_pwm_enable(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+       u8 currval, config, altbit, newval, minoff = 255;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       switch (reqval) {
+       case 0:
+               newval = 0x04;
+               break;
+       case 1:
+               newval = 0x07;
+               break;
+       case 2:
+               newval = 0x00;
+               minoff = 1;
+               break;
+       case 3:
+               newval = 0x00;
+               minoff = 0;
+               break;
+       case 255:
+               newval = 0x03;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       config = newval & 0x07;
+       altbit = (newval >> 3) & 0x01;
+
+       mutex_lock(&data->update_lock);
+       config = (config & param->mask[0]) << param->shift[0];
+       altbit = (altbit & param->mask[1]) << param->shift[1];
+       currval = read_byte(client, param->msb[0]);
+       newval = config | (currval & ~(param->mask[0] << param->shift[0]));
+       newval = altbit | (newval & ~(param->mask[1] << param->shift[1]));
+       data->reg[param->msb[0]] = newval;
+       write_byte(client, param->msb[0], newval);
+       if (minoff < 255) {
+               minoff = (minoff & param->mask[2]) << param->shift[2];
+               currval = read_byte(client, param->msb[2]);
+               newval =
+                   minoff | (currval & ~(param->mask[2] << param->shift[2]));
+               data->reg[param->msb[2]] = newval;
+               write_byte(client, param->msb[2], newval);
+       }
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static u32 asc7621_pwm_freq_map[] = {
+       10, 15, 23, 30, 38, 47, 62, 94,
+       23000, 24000, 25000, 26000, 27000, 28000, 29000, 30000
+};
+
+static ssize_t show_pwm_freq(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 regval =
+           (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+
+       regval = SENSORS_LIMIT(regval, 0, 15);
+
+       return sprintf(buf, "%u\n", asc7621_pwm_freq_map[regval]);
+}
+
+static ssize_t store_pwm_freq(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       unsigned long reqval;
+       u8 currval, newval = 255;
+       int i;
+
+       if (strict_strtoul(buf, 10, &reqval))
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(asc7621_pwm_freq_map); i++) {
+               if (reqval == asc7621_pwm_freq_map[i]) {
+                       newval = i;
+                       break;
+               }
+       }
+       if (newval == 255)
+               return -EINVAL;
+
+       newval = (newval & param->mask[0]) << param->shift[0];
+
+       mutex_lock(&data->update_lock);
+       currval = read_byte(client, param->msb[0]);
+       newval |= (currval & ~(param->mask[0] << param->shift[0]));
+       data->reg[param->msb[0]] = newval;
+       write_byte(client, param->msb[0], newval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static u32 asc7621_pwm_auto_spinup_map[] =  {
+       0, 100, 250, 400, 700, 1000, 2000, 4000
+};
+
+static ssize_t show_pwm_ast(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 regval =
+           (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+
+       regval = SENSORS_LIMIT(regval, 0, 7);
+
+       return sprintf(buf, "%u\n", asc7621_pwm_auto_spinup_map[regval]);
+
+}
+
+static ssize_t store_pwm_ast(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+       u8 currval, newval = 255;
+       u32 i;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(asc7621_pwm_auto_spinup_map); i++) {
+               if (reqval == asc7621_pwm_auto_spinup_map[i]) {
+                       newval = i;
+                       break;
+               }
+       }
+       if (newval == 255)
+               return -EINVAL;
+
+       newval = (newval & param->mask[0]) << param->shift[0];
+
+       mutex_lock(&data->update_lock);
+       currval = read_byte(client, param->msb[0]);
+       newval |= (currval & ~(param->mask[0] << param->shift[0]));
+       data->reg[param->msb[0]] = newval;
+       write_byte(client, param->msb[0], newval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static u32 asc7621_temp_smoothing_time_map[] = {
+       35000, 17600, 11800, 7000, 4400, 3000, 1600, 800
+};
+
+static ssize_t show_temp_st(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       SETUP_SHOW_data_param(dev, attr);
+       u8 regval =
+           (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
+       regval = SENSORS_LIMIT(regval, 0, 7);
+
+       return sprintf(buf, "%u\n", asc7621_temp_smoothing_time_map[regval]);
+}
+
+static ssize_t store_temp_st(struct device *dev,
+                            struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       SETUP_STORE_data_param(dev, attr);
+       long reqval;
+       u8 currval, newval = 255;
+       u32 i;
+
+       if (strict_strtol(buf, 10, &reqval))
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(asc7621_temp_smoothing_time_map); i++) {
+               if (reqval == asc7621_temp_smoothing_time_map[i]) {
+                       newval = i;
+                       break;
+               }
+       }
+
+       if (newval == 255)
+               return -EINVAL;
+
+       newval = (newval & param->mask[0]) << param->shift[0];
+
+       mutex_lock(&data->update_lock);
+       currval = read_byte(client, param->msb[0]);
+       newval |= (currval & ~(param->mask[0] << param->shift[0]));
+       data->reg[param->msb[0]] = newval;
+       write_byte(client, param->msb[0], newval);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * End of data handlers
+ *
+ * These defines do nothing more than make the table easier
+ * to read when wrapped at column 80.
+ */
+
+/*
+ * Creates a variable length array inititalizer.
+ * VAA(1,3,5,7) would produce {1,3,5,7}
+ */
+#define VAA(args...) {args}
+
+#define PREAD(name, n, pri, rm, rl, m, s, r) \
+       {.sda = SENSOR_ATTR(name, S_IRUGO, show_##r, NULL, n), \
+         .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \
+         .shift[0] = s,}
+
+#define PWRITE(name, n, pri, rm, rl, m, s, r) \
+       {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \
+         .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \
+         .shift[0] = s,}
+
+/*
+ * PWRITEM assumes that the initializers for the .msb, .lsb, .mask and .shift
+ * were created using the VAA macro.
+ */
+#define PWRITEM(name, n, pri, rm, rl, m, s, r) \
+       {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \
+         .priority = pri, .msb = rm, .lsb = rl, .mask = m, .shift = s,}
+
+static struct asc7621_param asc7621_params[] = {
+       PREAD(in0_input, 0, PRI_HIGH, 0x20, 0x13, 0, 0, in10),
+       PREAD(in1_input, 1, PRI_HIGH, 0x21, 0x18, 0, 0, in10),
+       PREAD(in2_input, 2, PRI_HIGH, 0x22, 0x11, 0, 0, in10),
+       PREAD(in3_input, 3, PRI_HIGH, 0x23, 0x12, 0, 0, in10),
+       PREAD(in4_input, 4, PRI_HIGH, 0x24, 0x14, 0, 0, in10),
+
+       PWRITE(in0_min, 0, PRI_LOW, 0x44, 0, 0, 0, in8),
+       PWRITE(in1_min, 1, PRI_LOW, 0x46, 0, 0, 0, in8),
+       PWRITE(in2_min, 2, PRI_LOW, 0x48, 0, 0, 0, in8),
+       PWRITE(in3_min, 3, PRI_LOW, 0x4a, 0, 0, 0, in8),
+       PWRITE(in4_min, 4, PRI_LOW, 0x4c, 0, 0, 0, in8),
+
+       PWRITE(in0_max, 0, PRI_LOW, 0x45, 0, 0, 0, in8),
+       PWRITE(in1_max, 1, PRI_LOW, 0x47, 0, 0, 0, in8),
+       PWRITE(in2_max, 2, PRI_LOW, 0x49, 0, 0, 0, in8),
+       PWRITE(in3_max, 3, PRI_LOW, 0x4b, 0, 0, 0, in8),
+       PWRITE(in4_max, 4, PRI_LOW, 0x4d, 0, 0, 0, in8),
+
+       PREAD(in0_alarm, 0, PRI_LOW, 0x41, 0, 0x01, 0, bitmask),
+       PREAD(in1_alarm, 1, PRI_LOW, 0x41, 0, 0x01, 1, bitmask),
+       PREAD(in2_alarm, 2, PRI_LOW, 0x41, 0, 0x01, 2, bitmask),
+       PREAD(in3_alarm, 3, PRI_LOW, 0x41, 0, 0x01, 3, bitmask),
+       PREAD(in4_alarm, 4, PRI_LOW, 0x42, 0, 0x01, 0, bitmask),
+
+       PREAD(fan1_input, 0, PRI_HIGH, 0x29, 0x28, 0, 0, fan16),
+       PREAD(fan2_input, 1, PRI_HIGH, 0x2b, 0x2a, 0, 0, fan16),
+       PREAD(fan3_input, 2, PRI_HIGH, 0x2d, 0x2c, 0, 0, fan16),
+       PREAD(fan4_input, 3, PRI_HIGH, 0x2f, 0x2e, 0, 0, fan16),
+
+       PWRITE(fan1_min, 0, PRI_LOW, 0x55, 0x54, 0, 0, fan16),
+       PWRITE(fan2_min, 1, PRI_LOW, 0x57, 0x56, 0, 0, fan16),
+       PWRITE(fan3_min, 2, PRI_LOW, 0x59, 0x58, 0, 0, fan16),
+       PWRITE(fan4_min, 3, PRI_LOW, 0x5b, 0x5a, 0, 0, fan16),
+
+       PREAD(fan1_alarm, 0, PRI_LOW, 0x42, 0, 0x01, 0, bitmask),
+       PREAD(fan2_alarm, 1, PRI_LOW, 0x42, 0, 0x01, 1, bitmask),
+       PREAD(fan3_alarm, 2, PRI_LOW, 0x42, 0, 0x01, 2, bitmask),
+       PREAD(fan4_alarm, 3, PRI_LOW, 0x42, 0, 0x01, 3, bitmask),
+
+       PREAD(temp1_input, 0, PRI_HIGH, 0x25, 0x10, 0, 0, temp10),
+       PREAD(temp2_input, 1, PRI_HIGH, 0x26, 0x15, 0, 0, temp10),
+       PREAD(temp3_input, 2, PRI_HIGH, 0x27, 0x16, 0, 0, temp10),
+       PREAD(temp4_input, 3, PRI_HIGH, 0x33, 0x17, 0, 0, temp10),
+       PREAD(temp5_input, 4, PRI_HIGH, 0xf7, 0xf6, 0, 0, temp10),
+       PREAD(temp6_input, 5, PRI_HIGH, 0xf9, 0xf8, 0, 0, temp10),
+       PREAD(temp7_input, 6, PRI_HIGH, 0xfb, 0xfa, 0, 0, temp10),
+       PREAD(temp8_input, 7, PRI_HIGH, 0xfd, 0xfc, 0, 0, temp10),
+
+       PWRITE(temp1_min, 0, PRI_LOW, 0x4e, 0, 0, 0, temp8),
+       PWRITE(temp2_min, 1, PRI_LOW, 0x50, 0, 0, 0, temp8),
+       PWRITE(temp3_min, 2, PRI_LOW, 0x52, 0, 0, 0, temp8),
+       PWRITE(temp4_min, 3, PRI_LOW, 0x34, 0, 0, 0, temp8),
+
+       PWRITE(temp1_max, 0, PRI_LOW, 0x4f, 0, 0, 0, temp8),
+       PWRITE(temp2_max, 1, PRI_LOW, 0x51, 0, 0, 0, temp8),
+       PWRITE(temp3_max, 2, PRI_LOW, 0x53, 0, 0, 0, temp8),
+       PWRITE(temp4_max, 3, PRI_LOW, 0x35, 0, 0, 0, temp8),
+
+       PREAD(temp1_alarm, 0, PRI_LOW, 0x41, 0, 0x01, 4, bitmask),
+       PREAD(temp2_alarm, 1, PRI_LOW, 0x41, 0, 0x01, 5, bitmask),
+       PREAD(temp3_alarm, 2, PRI_LOW, 0x41, 0, 0x01, 6, bitmask),
+       PREAD(temp4_alarm, 3, PRI_LOW, 0x43, 0, 0x01, 0, bitmask),
+
+       PWRITE(temp1_source, 0, PRI_LOW, 0x02, 0, 0x07, 4, bitmask),
+       PWRITE(temp2_source, 1, PRI_LOW, 0x02, 0, 0x07, 0, bitmask),
+       PWRITE(temp3_source, 2, PRI_LOW, 0x03, 0, 0x07, 4, bitmask),
+       PWRITE(temp4_source, 3, PRI_LOW, 0x03, 0, 0x07, 0, bitmask),
+
+       PWRITE(temp1_smoothing_enable, 0, PRI_LOW, 0x62, 0, 0x01, 3, bitmask),
+       PWRITE(temp2_smoothing_enable, 1, PRI_LOW, 0x63, 0, 0x01, 7, bitmask),
+       PWRITE(temp3_smoothing_enable, 2, PRI_LOW, 0x64, 0, 0x01, 3, bitmask),
+       PWRITE(temp4_smoothing_enable, 3, PRI_LOW, 0x3c, 0, 0x01, 3, bitmask),
+
+       PWRITE(temp1_smoothing_time, 0, PRI_LOW, 0x62, 0, 0x07, 0, temp_st),
+       PWRITE(temp2_smoothing_time, 1, PRI_LOW, 0x63, 0, 0x07, 4, temp_st),
+       PWRITE(temp3_smoothing_time, 2, PRI_LOW, 0x63, 0, 0x07, 0, temp_st),
+       PWRITE(temp4_smoothing_time, 3, PRI_LOW, 0x3c, 0, 0x07, 0, temp_st),
+
+       PWRITE(temp1_auto_point1_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4,
+              bitmask),
+       PWRITE(temp2_auto_point1_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0,
+              bitmask),
+       PWRITE(temp3_auto_point1_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4,
+              bitmask),
+       PWRITE(temp4_auto_point1_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0,
+              bitmask),
+
+       PREAD(temp1_auto_point2_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4,
+             bitmask),
+       PREAD(temp2_auto_point2_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0,
+             bitmask),
+       PREAD(temp3_auto_point2_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4,
+             bitmask),
+       PREAD(temp4_auto_point2_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0,
+             bitmask),
+
+       PWRITE(temp1_auto_point1_temp, 0, PRI_LOW, 0x67, 0, 0, 0, temp8),
+       PWRITE(temp2_auto_point1_temp, 1, PRI_LOW, 0x68, 0, 0, 0, temp8),
+       PWRITE(temp3_auto_point1_temp, 2, PRI_LOW, 0x69, 0, 0, 0, temp8),
+       PWRITE(temp4_auto_point1_temp, 3, PRI_LOW, 0x3b, 0, 0, 0, temp8),
+
+       PWRITEM(temp1_auto_point2_temp, 0, PRI_LOW, VAA(0x5f, 0x67), VAA(0),
+               VAA(0x0f), VAA(4), ap2_temp),
+       PWRITEM(temp2_auto_point2_temp, 1, PRI_LOW, VAA(0x60, 0x68), VAA(0),
+               VAA(0x0f), VAA(4), ap2_temp),
+       PWRITEM(temp3_auto_point2_temp, 2, PRI_LOW, VAA(0x61, 0x69), VAA(0),
+               VAA(0x0f), VAA(4), ap2_temp),
+       PWRITEM(temp4_auto_point2_temp, 3, PRI_LOW, VAA(0x3c, 0x3b), VAA(0),
+               VAA(0x0f), VAA(4), ap2_temp),
+
+       PWRITE(temp1_crit, 0, PRI_LOW, 0x6a, 0, 0, 0, temp8),
+       PWRITE(temp2_crit, 1, PRI_LOW, 0x6b, 0, 0, 0, temp8),
+       PWRITE(temp3_crit, 2, PRI_LOW, 0x6c, 0, 0, 0, temp8),
+       PWRITE(temp4_crit, 3, PRI_LOW, 0x3d, 0, 0, 0, temp8),
+
+       PWRITE(temp5_enable, 4, PRI_LOW, 0x0e, 0, 0x01, 0, bitmask),
+       PWRITE(temp6_enable, 5, PRI_LOW, 0x0e, 0, 0x01, 1, bitmask),
+       PWRITE(temp7_enable, 6, PRI_LOW, 0x0e, 0, 0x01, 2, bitmask),
+       PWRITE(temp8_enable, 7, PRI_LOW, 0x0e, 0, 0x01, 3, bitmask),
+
+       PWRITE(remote1_offset, 0, PRI_LOW, 0x1c, 0, 0, 0, temp62),
+       PWRITE(remote2_offset, 1, PRI_LOW, 0x1d, 0, 0, 0, temp62),
+
+       PWRITE(pwm1, 0, PRI_HIGH, 0x30, 0, 0, 0, u8),
+       PWRITE(pwm2, 1, PRI_HIGH, 0x31, 0, 0, 0, u8),
+       PWRITE(pwm3, 2, PRI_HIGH, 0x32, 0, 0, 0, u8),
+
+       PWRITE(pwm1_invert, 0, PRI_LOW, 0x5c, 0, 0x01, 4, bitmask),
+       PWRITE(pwm2_invert, 1, PRI_LOW, 0x5d, 0, 0x01, 4, bitmask),
+       PWRITE(pwm3_invert, 2, PRI_LOW, 0x5e, 0, 0x01, 4, bitmask),
+
+       PWRITEM(pwm1_enable, 0, PRI_LOW, VAA(0x5c, 0x5c, 0x62), VAA(0, 0, 0),
+               VAA(0x07, 0x01, 0x01), VAA(5, 3, 5), pwm_enable),
+       PWRITEM(pwm2_enable, 1, PRI_LOW, VAA(0x5d, 0x5d, 0x62), VAA(0, 0, 0),
+               VAA(0x07, 0x01, 0x01), VAA(5, 3, 6), pwm_enable),
+       PWRITEM(pwm3_enable, 2, PRI_LOW, VAA(0x5e, 0x5e, 0x62), VAA(0, 0, 0),
+               VAA(0x07, 0x01, 0x01), VAA(5, 3, 7), pwm_enable),
+
+       PWRITEM(pwm1_auto_channels, 0, PRI_LOW, VAA(0x5c, 0x5c), VAA(0, 0),
+               VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
+       PWRITEM(pwm2_auto_channels, 1, PRI_LOW, VAA(0x5d, 0x5d), VAA(0, 0),
+               VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
+       PWRITEM(pwm3_auto_channels, 2, PRI_LOW, VAA(0x5e, 0x5e), VAA(0, 0),
+               VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
+
+       PWRITE(pwm1_auto_point1_pwm, 0, PRI_LOW, 0x64, 0, 0, 0, u8),
+       PWRITE(pwm2_auto_point1_pwm, 1, PRI_LOW, 0x65, 0, 0, 0, u8),
+       PWRITE(pwm3_auto_point1_pwm, 2, PRI_LOW, 0x66, 0, 0, 0, u8),
+
+       PWRITE(pwm1_auto_point2_pwm, 0, PRI_LOW, 0x38, 0, 0, 0, u8),
+       PWRITE(pwm2_auto_point2_pwm, 1, PRI_LOW, 0x39, 0, 0, 0, u8),
+       PWRITE(pwm3_auto_point2_pwm, 2, PRI_LOW, 0x3a, 0, 0, 0, u8),
+
+       PWRITE(pwm1_freq, 0, PRI_LOW, 0x5f, 0, 0x0f, 0, pwm_freq),
+       PWRITE(pwm2_freq, 1, PRI_LOW, 0x60, 0, 0x0f, 0, pwm_freq),
+       PWRITE(pwm3_freq, 2, PRI_LOW, 0x61, 0, 0x0f, 0, pwm_freq),
+
+       PREAD(pwm1_auto_zone_assigned, 0, PRI_LOW, 0, 0, 0x03, 2, bitmask),
+       PREAD(pwm2_auto_zone_assigned, 1, PRI_LOW, 0, 0, 0x03, 4, bitmask),
+       PREAD(pwm3_auto_zone_assigned, 2, PRI_LOW, 0, 0, 0x03, 6, bitmask),
+
+       PWRITE(pwm1_auto_spinup_time, 0, PRI_LOW, 0x5c, 0, 0x07, 0, pwm_ast),
+       PWRITE(pwm2_auto_spinup_time, 1, PRI_LOW, 0x5d, 0, 0x07, 0, pwm_ast),
+       PWRITE(pwm3_auto_spinup_time, 2, PRI_LOW, 0x5e, 0, 0x07, 0, pwm_ast),
+
+       PWRITE(peci_enable, 0, PRI_LOW, 0x40, 0, 0x01, 4, bitmask),
+       PWRITE(peci_avg, 0, PRI_LOW, 0x36, 0, 0x07, 0, bitmask),
+       PWRITE(peci_domain, 0, PRI_LOW, 0x36, 0, 0x01, 3, bitmask),
+       PWRITE(peci_legacy, 0, PRI_LOW, 0x36, 0, 0x01, 4, bitmask),
+       PWRITE(peci_diode, 0, PRI_LOW, 0x0e, 0, 0x07, 4, bitmask),
+       PWRITE(peci_4domain, 0, PRI_LOW, 0x0e, 0, 0x01, 4, bitmask),
+
+};
+
+static struct asc7621_data *asc7621_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct asc7621_data *data = i2c_get_clientdata(client);
+       int i;
+
+/*
+ * The asc7621 chips guarantee consistent reads of multi-byte values
+ * regardless of the order of the reads.  No special logic is needed
+ * so we can just read the registers in whatever  order they appear
+ * in the asc7621_params array.
+ */
+
+       mutex_lock(&data->update_lock);
+
+       /* Read all the high priority registers */
+
+       if (!data->valid ||
+           time_after(jiffies, data->last_high_reading + INTERVAL_HIGH)) {
+
+               for (i = 0; i < ARRAY_SIZE(asc7621_register_priorities); i++) {
+                       if (asc7621_register_priorities[i] == PRI_HIGH) {
+                               data->reg[i] =
+                                   i2c_smbus_read_byte_data(client, i) & 0xff;
+                       }
+               }
+               data->last_high_reading = jiffies;
+       };                      /* last_reading */
+
+       /* Read all the low priority registers. */
+
+       if (!data->valid ||
+           time_after(jiffies, data->last_low_reading + INTERVAL_LOW)) {
+
+               for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+                       if (asc7621_register_priorities[i] == PRI_LOW) {
+                               data->reg[i] =
+                                   i2c_smbus_read_byte_data(client, i) & 0xff;
+                       }
+               }
+               data->last_low_reading = jiffies;
+       };                      /* last_reading */
+
+       data->valid = 1;
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
+/*
+ * Standard detection and initialization below
+ *
+ * Helper function that checks if an address is valid
+ * for a particular chip.
+ */
+
+static inline int valid_address_for_chip(int chip_type, int address)
+{
+       int i;
+
+       for (i = 0; asc7621_chips[chip_type].addresses[i] != I2C_CLIENT_END;
+            i++) {
+               if (asc7621_chips[chip_type].addresses[i] == address)
+                       return 1;
+       }
+       return 0;
+}
+
+static void asc7621_init_client(struct i2c_client *client)
+{
+       int value;
+
+       /* Warn if part was not "READY" */
+
+       value = read_byte(client, 0x40);
+
+       if (value & 0x02) {
+               dev_err(&client->dev,
+                       "Client (%d,0x%02x) config is locked.\n",
+                       i2c_adapter_id(client->adapter), client->addr);
+       };
+       if (!(value & 0x04)) {
+               dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n",
+                       i2c_adapter_id(client->adapter), client->addr);
+       };
+
+/*
+ * Start monitoring
+ *
+ * Try to clear LOCK, Set START, save everything else
+ */
+       value = (value & ~0x02) | 0x01;
+       write_byte(client, 0x40, value & 0xff);
+
+}
+
+static int
+asc7621_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct asc7621_data *data;
+       int i, err;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -EIO;
+
+       data = kzalloc(sizeof(struct asc7621_data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, data);
+       data->valid = 0;
+       mutex_init(&data->update_lock);
+
+       /* Initialize the asc7621 chip */
+       asc7621_init_client(client);
+
+       /* Create the sysfs entries */
+       for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+               err =
+                   device_create_file(&client->dev,
+                                      &(asc7621_params[i].sda.dev_attr));
+               if (err)
+                       goto exit_remove;
+       }
+
+       data->class_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->class_dev)) {
+               err = PTR_ERR(data->class_dev);
+               goto exit_remove;
+       }
+
+       return 0;
+
+exit_remove:
+       for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+               device_remove_file(&client->dev,
+                                  &(asc7621_params[i].sda.dev_attr));
+       }
+
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+       return err;
+}
+
+static int asc7621_detect(struct i2c_client *client,
+                         struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       int company, verstep, chip_index;
+       struct device *dev;
+
+       dev = &client->dev;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       for (chip_index = FIRST_CHIP; chip_index <= LAST_CHIP; chip_index++) {
+
+               if (!valid_address_for_chip(chip_index, client->addr))
+                       continue;
+
+               company = read_byte(client,
+                       asc7621_chips[chip_index].company_reg);
+               verstep = read_byte(client,
+                       asc7621_chips[chip_index].verstep_reg);
+
+               if (company == asc7621_chips[chip_index].company_id &&
+                   verstep == asc7621_chips[chip_index].verstep_id) {
+                       strlcpy(client->name, asc7621_chips[chip_index].name,
+                               I2C_NAME_SIZE);
+                       strlcpy(info->type, asc7621_chips[chip_index].name,
+                               I2C_NAME_SIZE);
+
+                       dev_info(&adapter->dev, "Matched %s\n",
+                                asc7621_chips[chip_index].name);
+                       return 0;
+               }
+       }
+
+       return -ENODEV;
+}
+
+static int asc7621_remove(struct i2c_client *client)
+{
+       struct asc7621_data *data = i2c_get_clientdata(client);
+       int i;
+
+       hwmon_device_unregister(data->class_dev);
+
+       for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+               device_remove_file(&client->dev,
+                                  &(asc7621_params[i].sda.dev_attr));
+       }
+
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+       return 0;
+}
+
+static const struct i2c_device_id asc7621_id[] = {
+       {"asc7621", asc7621},
+       {"asc7621a", asc7621a},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, asc7621_id);
+
+static struct i2c_driver asc7621_driver = {
+       .class = I2C_CLASS_HWMON,
+       .driver = {
+               .name = "asc7621",
+       },
+       .probe = asc7621_probe,
+       .remove = asc7621_remove,
+       .id_table = asc7621_id,
+       .detect = asc7621_detect,
+       .address_list = normal_i2c,
+};
+
+static int __init sm_asc7621_init(void)
+{
+       int i, j;
+/*
+ * Collect all the registers needed into a single array.
+ * This way, if a register isn't actually used for anything,
+ * we don't retrieve it.
+ */
+
+       for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
+               for (j = 0; j < ARRAY_SIZE(asc7621_params[i].msb); j++)
+                       asc7621_register_priorities[asc7621_params[i].msb[j]] =
+                           asc7621_params[i].priority;
+               for (j = 0; j < ARRAY_SIZE(asc7621_params[i].lsb); j++)
+                       asc7621_register_priorities[asc7621_params[i].lsb[j]] =
+                           asc7621_params[i].priority;
+       }
+       return i2c_add_driver(&asc7621_driver);
+}
+
+static void __exit sm_asc7621_exit(void)
+{
+       i2c_del_driver(&asc7621_driver);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("George Joseph");
+MODULE_DESCRIPTION("Andigilog aSC7621 and aSC7621a driver");
+
+module_init(sm_asc7621_init);
+module_exit(sm_asc7621_exit);
index fa0728232e7179e6afc8b1b529948d13766d297e..0627f7a5b9b822462f36341d8889f6bfb33c1986 100644 (file)
@@ -267,7 +267,7 @@ struct fschmd_data {
        struct list_head list; /* member of the watchdog_data_list */
        struct kref kref;
        struct miscdevice watchdog_miscdev;
-       int kind;
+       enum chips kind;
        unsigned long watchdog_is_open;
        char watchdog_expect_close;
        char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
@@ -325,8 +325,7 @@ static ssize_t show_in_value(struct device *dev,
        int index = to_sensor_dev_attr(devattr)->index;
        struct fschmd_data *data = fschmd_update_device(dev);
 
-       /* fscher / fschrc - 1 as data->kind is an array index, not a chips */
-       if (data->kind == (fscher - 1) || data->kind >= (fschrc - 1))
+       if (data->kind == fscher || data->kind >= fschrc)
                return sprintf(buf, "%d\n", (data->volt[index] * dmi_vref *
                        dmi_mult[index]) / 255 + dmi_offset[index]);
        else
@@ -492,7 +491,7 @@ static ssize_t show_pwm_auto_point1_pwm(struct device *dev,
        int val = data->fan_min[index];
 
        /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */
-       if (val || data->kind == fscsyl - 1)
+       if (val || data->kind == fscsyl)
                val = val / 2 + 128;
 
        return sprintf(buf, "%d\n", val);
@@ -506,7 +505,7 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev,
        unsigned long v = simple_strtoul(buf, NULL, 10);
 
        /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */
-       if (v || data->kind == fscsyl - 1) {
+       if (v || data->kind == fscsyl) {
                v = SENSORS_LIMIT(v, 128, 255);
                v = (v - 128) * 2 + 1;
        }
@@ -1037,7 +1036,7 @@ static int fschmd_detect(struct i2c_client *client,
        else
                return -ENODEV;
 
-       strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE);
+       strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE);
 
        return 0;
 }
@@ -1065,6 +1064,7 @@ static int fschmd_probe(struct i2c_client *client,
           (where the client is found through a data ptr instead of the
           otherway around) */
        data->client = client;
+       data->kind = kind;
 
        if (kind == fscpos) {
                /* The Poseidon has hardwired temp limits, fill these
@@ -1085,9 +1085,6 @@ static int fschmd_probe(struct i2c_client *client,
                }
        }
 
-       /* i2c kind goes from 1-6, we want from 0-5 to address arrays */
-       data->kind = kind - 1;
-
        /* Read in some never changing registers */
        data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
        data->global_control = i2c_smbus_read_byte_data(client,
index 19c01a49f6be09449488812ef85c6659fa70b03b..09ea12e0a55185b3e5d0699cf7922898752b70af 100644 (file)
@@ -68,7 +68,7 @@ struct g760a_data {
 #define PWM_FROM_CNT(cnt)      (0xff-(cnt))
 #define PWM_TO_CNT(pwm)                (0xff-(pwm))
 
-unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div)
+static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div)
 {
        return ((val == 0x00) ? 0 : ((clk*30)/(val*div)));
 }
index 0ffe84d190bbfabae0b7343ba6f3d39c078eabee..1002befd87d5c63268e8d2a4fb7f0513e52657d0 100644 (file)
@@ -1,40 +1,40 @@
 /*
   it87.c - Part of lm_sensors, Linux kernel modules for hardware
            monitoring.
-
   The IT8705F is an LPC-based Super I/O part that contains UARTs, a
   parallel port, an IR port, a MIDI port, a floppy controller, etc., in
   addition to an Environment Controller (Enhanced Hardware Monitor and
   Fan Controller)
-
   This driver supports only the Environment Controller in the IT8705F and
   similar parts.  The other devices are supported by different drivers.
-
   Supports: IT8705F  Super I/O chip w/LPC interface
             IT8712F  Super I/O chip w/LPC interface
             IT8716F  Super I/O chip w/LPC interface
             IT8718F  Super I/O chip w/LPC interface
             IT8720F  Super I/O chip w/LPC interface
             IT8726F  Super I/O chip w/LPC interface
             Sis950   A clone of the IT8705F
-
   Copyright (C) 2001 Chris Gauthron
   Copyright (C) 2005-2007 Jean Delvare <khali@linux-fr.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; 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.
-*/
*  it87.c - Part of lm_sensors, Linux kernel modules for hardware
*           monitoring.
+ *
*  The IT8705F is an LPC-based Super I/O part that contains UARTs, a
*  parallel port, an IR port, a MIDI port, a floppy controller, etc., in
*  addition to an Environment Controller (Enhanced Hardware Monitor and
*  Fan Controller)
+ *
*  This driver supports only the Environment Controller in the IT8705F and
*  similar parts.  The other devices are supported by different drivers.
+ *
*  Supports: IT8705F  Super I/O chip w/LPC interface
*            IT8712F  Super I/O chip w/LPC interface
*            IT8716F  Super I/O chip w/LPC interface
*            IT8718F  Super I/O chip w/LPC interface
*            IT8720F  Super I/O chip w/LPC interface
*            IT8726F  Super I/O chip w/LPC interface
*            Sis950   A clone of the IT8705F
+ *
*  Copyright (C) 2001 Chris Gauthron
*  Copyright (C) 2005-2010 Jean Delvare <khali@linux-fr.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; 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.
+ */
 
 #include <linux/module.h>
 #include <linux/init.h>
@@ -128,6 +128,7 @@ superio_exit(void)
 #define IT87_SIO_GPIO5_REG     0x29
 #define IT87_SIO_PINX2_REG     0x2c    /* Pin selection */
 #define IT87_SIO_VID_REG       0xfc    /* VID value */
+#define IT87_SIO_BEEP_PIN_REG  0xf6    /* Beep pin mapping */
 
 /* Update battery voltage after every reading if true */
 static int update_vbat;
@@ -187,9 +188,13 @@ static const u8 IT87_REG_FANX_MIN[]        = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };
 
 #define IT87_REG_VIN_ENABLE    0x50
 #define IT87_REG_TEMP_ENABLE   0x51
+#define IT87_REG_BEEP_ENABLE   0x5c
 
 #define IT87_REG_CHIPID        0x58
 
+#define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i))
+#define IT87_REG_AUTO_PWM(nr, i)  (0x65 + (nr) * 8 + (i))
+
 #define IN_TO_REG(val)  (SENSORS_LIMIT((((val) + 8)/16),0,255))
 #define IN_FROM_REG(val) ((val) * 16)
 
@@ -246,6 +251,7 @@ struct it87_sio_data {
        /* Values read from Super-I/O config space */
        u8 revision;
        u8 vid_value;
+       u8 beep_pin;
        /* Features skipped based on config or DMI */
        u8 skip_vid;
        u8 skip_fan;
@@ -279,9 +285,21 @@ struct it87_data {
        u8 vid;                 /* Register encoding, combined */
        u8 vrm;
        u32 alarms;             /* Register encoding, combined */
+       u8 beeps;               /* Register encoding */
        u8 fan_main_ctrl;       /* Register value */
        u8 fan_ctl;             /* Register value */
-       u8 manual_pwm_ctl[3];   /* manual PWM value set by user */
+
+       /* The following 3 arrays correspond to the same registers. The
+        * meaning of bits 6-0 depends on the value of bit 7, and we want
+        * to preserve settings on mode changes, so we have to track all
+        * values separately. */
+       u8 pwm_ctrl[3];         /* Register value */
+       u8 pwm_duty[3];         /* Manual PWM value set by user (bit 6-0) */
+       u8 pwm_temp_map[3];     /* PWM to temp. chan. mapping (bits 1-0) */
+
+       /* Automatic fan speed control registers */
+       u8 auto_pwm[3][4];      /* [nr][3] is hard-coded */
+       s8 auto_temp[3][5];     /* [nr][0] is point1_temp_hyst */
 };
 
 static inline int has_16bit_fans(const struct it87_data *data)
@@ -296,6 +314,15 @@ static inline int has_16bit_fans(const struct it87_data *data)
            || data->type == it8720;
 }
 
+static inline int has_old_autopwm(const struct it87_data *data)
+{
+       /* The old automatic fan speed control interface is implemented
+          by IT8705F chips up to revision F and IT8712F chips up to
+          revision G. */
+       return (data->type == it87 && data->revision < 0x03)
+           || (data->type == it8712 && data->revision < 0x08);
+}
+
 static int it87_probe(struct platform_device *pdev);
 static int __devexit it87_remove(struct platform_device *pdev);
 
@@ -352,7 +379,10 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
 
        mutex_lock(&data->update_lock);
        data->in_min[nr] = IN_TO_REG(val);
@@ -368,7 +398,10 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
 
        mutex_lock(&data->update_lock);
        data->in_max[nr] = IN_TO_REG(val);
@@ -441,7 +474,10 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
 
        mutex_lock(&data->update_lock);
        data->temp_high[nr] = TEMP_TO_REG(val);
@@ -456,7 +492,10 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
 
        mutex_lock(&data->update_lock);
        data->temp_low[nr] = TEMP_TO_REG(val);
@@ -483,8 +522,9 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = it87_update_device(dev);
-       u8 reg = data->sensor; /* In case the value is updated while we use it */
-       
+       u8 reg = data->sensor;          /* In case the value is updated while
+                                          we use it */
+
        if (reg & (1 << nr))
                return sprintf(buf, "3\n");  /* thermal diode */
        if (reg & (8 << nr))
@@ -498,7 +538,10 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
 
        mutex_lock(&data->update_lock);
 
@@ -511,9 +554,9 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr,
        }
        /* 3 = thermal diode; 4 = thermistor; 0 = disabled */
        if (val == 3)
-           data->sensor |= 1 << nr;
+               data->sensor |= 1 << nr;
        else if (val == 4)
-           data->sensor |= 8 << nr;
+               data->sensor |= 8 << nr;
        else if (val != 0) {
                mutex_unlock(&data->update_lock);
                return -EINVAL;
@@ -531,6 +574,19 @@ show_sensor_offset(2);
 show_sensor_offset(3);
 
 /* 3 Fans */
+
+static int pwm_mode(const struct it87_data *data, int nr)
+{
+       int ctrl = data->fan_main_ctrl & (1 << nr);
+
+       if (ctrl == 0)                                  /* Full speed */
+               return 0;
+       if (data->pwm_ctrl[nr] & 0x80)                  /* Automatic mode */
+               return 2;
+       else                                            /* Manual mode */
+               return 1;
+}
+
 static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
                char *buf)
 {
@@ -538,7 +594,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = it87_update_device(dev);
-       return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], 
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
                                DIV_FROM_REG(data->fan_div[nr])));
 }
 static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr,
@@ -548,8 +604,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = it87_update_device(dev);
-       return sprintf(buf,"%d\n",
-               FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])));
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+                               DIV_FROM_REG(data->fan_div[nr])));
 }
 static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
                char *buf)
@@ -560,14 +616,14 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr,
        struct it87_data *data = it87_update_device(dev);
        return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
 }
-static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr,
-               char *buf)
+static ssize_t show_pwm_enable(struct device *dev,
+               struct device_attribute *attr, char *buf)
 {
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        int nr = sensor_attr->index;
 
        struct it87_data *data = it87_update_device(dev);
-       return sprintf(buf,"%d\n", (data->fan_main_ctrl & (1 << nr)) ? 1 : 0);
+       return sprintf(buf, "%d\n", pwm_mode(data, nr));
 }
 static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
                char *buf)
@@ -576,7 +632,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = it87_update_device(dev);
-       return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]);
+       return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr]));
 }
 static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
                char *buf)
@@ -593,15 +649,24 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
        u8 reg;
 
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
+
        mutex_lock(&data->update_lock);
        reg = it87_read_value(data, IT87_REG_FAN_DIV);
        switch (nr) {
-       case 0: data->fan_div[nr] = reg & 0x07; break;
-       case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break;
-       case 2: data->fan_div[nr] = (reg & 0x40) ? 3 : 1; break;
+       case 0:
+               data->fan_div[nr] = reg & 0x07;
+               break;
+       case 1:
+               data->fan_div[nr] = (reg >> 3) & 0x07;
+               break;
+       case 2:
+               data->fan_div[nr] = (reg & 0x40) ? 3 : 1;
+               break;
        }
 
        data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
@@ -616,10 +681,13 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       unsigned long val;
        int min;
        u8 old;
 
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
        mutex_lock(&data->update_lock);
        old = it87_read_value(data, IT87_REG_FAN_DIV);
 
@@ -651,6 +719,32 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
        mutex_unlock(&data->update_lock);
        return count;
 }
+
+/* Returns 0 if OK, -EINVAL otherwise */
+static int check_trip_points(struct device *dev, int nr)
+{
+       const struct it87_data *data = dev_get_drvdata(dev);
+       int i, err = 0;
+
+       if (has_old_autopwm(data)) {
+               for (i = 0; i < 3; i++) {
+                       if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+                               err = -EINVAL;
+               }
+               for (i = 0; i < 2; i++) {
+                       if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1])
+                               err = -EINVAL;
+               }
+       }
+
+       if (err) {
+               dev_err(dev, "Inconsistent trip points, not switching to "
+                       "automatic mode\n");
+               dev_err(dev, "Adjust the trip points and try again\n");
+       }
+       return err;
+}
+
 static ssize_t set_pwm_enable(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
@@ -658,7 +752,16 @@ static ssize_t set_pwm_enable(struct device *dev,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 2)
+               return -EINVAL;
+
+       /* Check trip points before switching to automatic mode */
+       if (val == 2) {
+               if (check_trip_points(dev, nr) < 0)
+                       return -EINVAL;
+       }
 
        mutex_lock(&data->update_lock);
 
@@ -669,16 +772,18 @@ static ssize_t set_pwm_enable(struct device *dev,
                it87_write_value(data, IT87_REG_FAN_CTL, tmp | (1 << nr));
                /* set on/off mode */
                data->fan_main_ctrl &= ~(1 << nr);
-               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
-       } else if (val == 1) {
+               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+                                data->fan_main_ctrl);
+       } else {
+               if (val == 1)                           /* Manual mode */
+                       data->pwm_ctrl[nr] = data->pwm_duty[nr];
+               else                                    /* Automatic mode */
+                       data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr];
+               it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]);
                /* set SmartGuardian mode */
                data->fan_main_ctrl |= (1 << nr);
-               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
-               /* set saved pwm value, clear FAN_CTLX PWM mode bit */
-               it87_write_value(data, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr]));
-       } else {
-               mutex_unlock(&data->update_lock);
-               return -EINVAL;
+               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+                                data->fan_main_ctrl);
        }
 
        mutex_unlock(&data->update_lock);
@@ -691,15 +796,19 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
        int nr = sensor_attr->index;
 
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
 
-       if (val < 0 || val > 255)
+       if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 255)
                return -EINVAL;
 
        mutex_lock(&data->update_lock);
-       data->manual_pwm_ctl[nr] = val;
-       if (data->fan_main_ctrl & (1 << nr))
-               it87_write_value(data, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr]));
+       data->pwm_duty[nr] = PWM_TO_REG(val);
+       /* If we are in manual mode, write the duty cycle immediately;
+        * otherwise, just store it for later use. */
+       if (!(data->pwm_ctrl[nr] & 0x80)) {
+               data->pwm_ctrl[nr] = data->pwm_duty[nr];
+               it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]);
+       }
        mutex_unlock(&data->update_lock);
        return count;
 }
@@ -707,9 +816,12 @@ static ssize_t set_pwm_freq(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct it87_data *data = dev_get_drvdata(dev);
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       unsigned long val;
        int i;
 
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
        /* Search for the nearest available frequency */
        for (i = 0; i < 7; i++) {
                if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2)
@@ -724,6 +836,132 @@ static ssize_t set_pwm_freq(struct device *dev,
 
        return count;
 }
+static ssize_t show_pwm_temp_map(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+
+       struct it87_data *data = it87_update_device(dev);
+       int map;
+
+       if (data->pwm_temp_map[nr] < 3)
+               map = 1 << data->pwm_temp_map[nr];
+       else
+               map = 0;                        /* Should never happen */
+       return sprintf(buf, "%d\n", map);
+}
+static ssize_t set_pwm_temp_map(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+       int nr = sensor_attr->index;
+
+       struct it87_data *data = dev_get_drvdata(dev);
+       long val;
+       u8 reg;
+
+       /* This check can go away if we ever support automatic fan speed
+          control on newer chips. */
+       if (!has_old_autopwm(data)) {
+               dev_notice(dev, "Mapping change disabled for safety reasons\n");
+               return -EINVAL;
+       }
+
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       switch (val) {
+       case (1 << 0):
+               reg = 0x00;
+               break;
+       case (1 << 1):
+               reg = 0x01;
+               break;
+       case (1 << 2):
+               reg = 0x02;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mutex_lock(&data->update_lock);
+       data->pwm_temp_map[nr] = reg;
+       /* If we are in automatic mode, write the temp mapping immediately;
+        * otherwise, just store it for later use. */
+       if (data->pwm_ctrl[nr] & 0x80) {
+               data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr];
+               it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]);
+       }
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_auto_pwm(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct it87_data *data = it87_update_device(dev);
+       struct sensor_device_attribute_2 *sensor_attr =
+                       to_sensor_dev_attr_2(attr);
+       int nr = sensor_attr->nr;
+       int point = sensor_attr->index;
+
+       return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point]));
+}
+
+static ssize_t set_auto_pwm(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct it87_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sensor_attr =
+                       to_sensor_dev_attr_2(attr);
+       int nr = sensor_attr->nr;
+       int point = sensor_attr->index;
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 255)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       data->auto_pwm[nr][point] = PWM_TO_REG(val);
+       it87_write_value(data, IT87_REG_AUTO_PWM(nr, point),
+                        data->auto_pwm[nr][point]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_auto_temp(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct it87_data *data = it87_update_device(dev);
+       struct sensor_device_attribute_2 *sensor_attr =
+                       to_sensor_dev_attr_2(attr);
+       int nr = sensor_attr->nr;
+       int point = sensor_attr->index;
+
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point]));
+}
+
+static ssize_t set_auto_temp(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct it87_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sensor_attr =
+                       to_sensor_dev_attr_2(attr);
+       int nr = sensor_attr->nr;
+       int point = sensor_attr->index;
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0 || val < -128000 || val > 127000)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       data->auto_temp[nr][point] = TEMP_TO_REG(val);
+       it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point),
+                        data->auto_temp[nr][point]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
 
 #define show_fan_offset(offset)                                        \
 static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO,                \
@@ -744,7 +982,36 @@ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR,          \
                show_pwm, set_pwm, offset - 1);                         \
 static DEVICE_ATTR(pwm##offset##_freq,                                 \
                (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO),            \
-               show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL));
+               show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL));    \
+static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels_temp,            \
+               S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, \
+               offset - 1);                                            \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_pwm,             \
+               S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm,         \
+               offset - 1, 0);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point2_pwm,             \
+               S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm,         \
+               offset - 1, 1);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point3_pwm,             \
+               S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm,         \
+               offset - 1, 2);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point4_pwm,             \
+               S_IRUGO, show_auto_pwm, NULL, offset - 1, 3);           \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_temp,            \
+               S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp,       \
+               offset - 1, 1);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_temp_hyst,       \
+               S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp,       \
+               offset - 1, 0);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point2_temp,            \
+               S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp,       \
+               offset - 1, 2);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point3_temp,            \
+               S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp,       \
+               offset - 1, 3);                                         \
+static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point4_temp,            \
+               S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp,       \
+               offset - 1, 4);
 
 show_pwm_offset(1);
 show_pwm_offset(2);
@@ -775,7 +1042,10 @@ static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr,
        struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
        int nr = sensor_attr->index;
        struct it87_data *data = dev_get_drvdata(dev);
-       int val = simple_strtol(buf, NULL, 10);
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
 
        mutex_lock(&data->update_lock);
        data->fan_min[nr] = FAN16_TO_REG(val);
@@ -805,7 +1075,8 @@ show_fan16_offset(4);
 show_fan16_offset(5);
 
 /* Alarms */
-static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
+               char *buf)
 {
        struct it87_data *data = it87_update_device(dev);
        return sprintf(buf, "%u\n", data->alarms);
@@ -836,27 +1107,78 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16);
 static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17);
 static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18);
 
-static ssize_t
-show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t show_beep(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       int bitnr = to_sensor_dev_attr(attr)->index;
+       struct it87_data *data = it87_update_device(dev);
+       return sprintf(buf, "%u\n", (data->beeps >> bitnr) & 1);
+}
+static ssize_t set_beep(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int bitnr = to_sensor_dev_attr(attr)->index;
+       struct it87_data *data = dev_get_drvdata(dev);
+       long val;
+
+       if (strict_strtol(buf, 10, &val) < 0
+        || (val != 0 && val != 1))
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE);
+       if (val)
+               data->beeps |= (1 << bitnr);
+       else
+               data->beeps &= ~(1 << bitnr);
+       it87_write_value(data, IT87_REG_BEEP_ENABLE, data->beeps);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR,
+                         show_beep, set_beep, 1);
+static SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO, show_beep, NULL, 1);
+static SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO, show_beep, NULL, 1);
+/* fanX_beep writability is set later */
+static SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0);
+static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR,
+                         show_beep, set_beep, 2);
+static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_beep, NULL, 2);
+
+static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr,
+               char *buf)
 {
        struct it87_data *data = dev_get_drvdata(dev);
        return sprintf(buf, "%u\n", data->vrm);
 }
-static ssize_t
-store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
 {
        struct it87_data *data = dev_get_drvdata(dev);
-       u32 val;
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
 
-       val = simple_strtoul(buf, NULL, 10);
        data->vrm = val;
 
        return count;
 }
 static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
 
-static ssize_t
-show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
+               char *buf)
 {
        struct it87_data *data = it87_update_device(dev);
        return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
@@ -931,51 +1253,176 @@ static const struct attribute_group it87_group = {
        .attrs = it87_attributes,
 };
 
-static struct attribute *it87_attributes_opt[] = {
+static struct attribute *it87_attributes_beep[] = {
+       &sensor_dev_attr_in0_beep.dev_attr.attr,
+       &sensor_dev_attr_in1_beep.dev_attr.attr,
+       &sensor_dev_attr_in2_beep.dev_attr.attr,
+       &sensor_dev_attr_in3_beep.dev_attr.attr,
+       &sensor_dev_attr_in4_beep.dev_attr.attr,
+       &sensor_dev_attr_in5_beep.dev_attr.attr,
+       &sensor_dev_attr_in6_beep.dev_attr.attr,
+       &sensor_dev_attr_in7_beep.dev_attr.attr,
+
+       &sensor_dev_attr_temp1_beep.dev_attr.attr,
+       &sensor_dev_attr_temp2_beep.dev_attr.attr,
+       &sensor_dev_attr_temp3_beep.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group it87_group_beep = {
+       .attrs = it87_attributes_beep,
+};
+
+static struct attribute *it87_attributes_fan16[5][3+1] = { {
        &sensor_dev_attr_fan1_input16.dev_attr.attr,
        &sensor_dev_attr_fan1_min16.dev_attr.attr,
+       &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+       NULL
+}, {
        &sensor_dev_attr_fan2_input16.dev_attr.attr,
        &sensor_dev_attr_fan2_min16.dev_attr.attr,
+       &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+       NULL
+}, {
        &sensor_dev_attr_fan3_input16.dev_attr.attr,
        &sensor_dev_attr_fan3_min16.dev_attr.attr,
+       &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+       NULL
+}, {
        &sensor_dev_attr_fan4_input16.dev_attr.attr,
        &sensor_dev_attr_fan4_min16.dev_attr.attr,
+       &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+       NULL
+}, {
        &sensor_dev_attr_fan5_input16.dev_attr.attr,
        &sensor_dev_attr_fan5_min16.dev_attr.attr,
+       &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+       NULL
+} };
+
+static const struct attribute_group it87_group_fan16[5] = {
+       { .attrs = it87_attributes_fan16[0] },
+       { .attrs = it87_attributes_fan16[1] },
+       { .attrs = it87_attributes_fan16[2] },
+       { .attrs = it87_attributes_fan16[3] },
+       { .attrs = it87_attributes_fan16[4] },
+};
 
+static struct attribute *it87_attributes_fan[3][4+1] = { {
        &sensor_dev_attr_fan1_input.dev_attr.attr,
        &sensor_dev_attr_fan1_min.dev_attr.attr,
        &sensor_dev_attr_fan1_div.dev_attr.attr,
+       &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+       NULL
+}, {
        &sensor_dev_attr_fan2_input.dev_attr.attr,
        &sensor_dev_attr_fan2_min.dev_attr.attr,
        &sensor_dev_attr_fan2_div.dev_attr.attr,
+       &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+       NULL
+}, {
        &sensor_dev_attr_fan3_input.dev_attr.attr,
        &sensor_dev_attr_fan3_min.dev_attr.attr,
        &sensor_dev_attr_fan3_div.dev_attr.attr,
-
-       &sensor_dev_attr_fan1_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan2_alarm.dev_attr.attr,
        &sensor_dev_attr_fan3_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan4_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+       NULL
+} };
+
+static const struct attribute_group it87_group_fan[3] = {
+       { .attrs = it87_attributes_fan[0] },
+       { .attrs = it87_attributes_fan[1] },
+       { .attrs = it87_attributes_fan[2] },
+};
+
+static const struct attribute_group *
+it87_get_fan_group(const struct it87_data *data)
+{
+       return has_16bit_fans(data) ? it87_group_fan16 : it87_group_fan;
+}
 
+static struct attribute *it87_attributes_pwm[3][4+1] = { {
        &sensor_dev_attr_pwm1_enable.dev_attr.attr,
-       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
-       &sensor_dev_attr_pwm3_enable.dev_attr.attr,
        &sensor_dev_attr_pwm1.dev_attr.attr,
-       &sensor_dev_attr_pwm2.dev_attr.attr,
-       &sensor_dev_attr_pwm3.dev_attr.attr,
        &dev_attr_pwm1_freq.attr,
+       &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+       NULL
+}, {
+       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm2.dev_attr.attr,
        &dev_attr_pwm2_freq.attr,
+       &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
+       NULL
+}, {
+       &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm3.dev_attr.attr,
        &dev_attr_pwm3_freq.attr,
+       &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
+       NULL
+} };
+
+static const struct attribute_group it87_group_pwm[3] = {
+       { .attrs = it87_attributes_pwm[0] },
+       { .attrs = it87_attributes_pwm[1] },
+       { .attrs = it87_attributes_pwm[2] },
+};
 
+static struct attribute *it87_attributes_autopwm[3][9+1] = { {
+       &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+       NULL
+}, {
+       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
+       NULL
+}, {
+       &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
+       NULL
+} };
+
+static const struct attribute_group it87_group_autopwm[3] = {
+       { .attrs = it87_attributes_autopwm[0] },
+       { .attrs = it87_attributes_autopwm[1] },
+       { .attrs = it87_attributes_autopwm[2] },
+};
+
+static struct attribute *it87_attributes_fan_beep[] = {
+       &sensor_dev_attr_fan1_beep.dev_attr.attr,
+       &sensor_dev_attr_fan2_beep.dev_attr.attr,
+       &sensor_dev_attr_fan3_beep.dev_attr.attr,
+       &sensor_dev_attr_fan4_beep.dev_attr.attr,
+       &sensor_dev_attr_fan5_beep.dev_attr.attr,
+};
+
+static struct attribute *it87_attributes_vid[] = {
        &dev_attr_vrm.attr,
        &dev_attr_cpu0_vid.attr,
        NULL
 };
 
-static const struct attribute_group it87_group_opt = {
-       .attrs = it87_attributes_opt,
+static const struct attribute_group it87_group_vid = {
+       .attrs = it87_attributes_vid,
 };
 
 /* SuperIO detection - will change isa_address if a chip is found */
@@ -1035,6 +1482,10 @@ static int __init it87_find(unsigned short *address,
        if (sio_data->type == it87) {
                /* The IT8705F doesn't have VID pins at all */
                sio_data->skip_vid = 1;
+
+               /* The IT8705F has a different LD number for GPIO */
+               superio_select(5);
+               sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
        } else {
                int reg;
 
@@ -1068,7 +1519,11 @@ static int __init it87_find(unsigned short *address,
                        pr_info("it87: in3 is VCC (+5V)\n");
                if (reg & (1 << 1))
                        pr_info("it87: in7 is VCCH (+5V Stand-By)\n");
+
+               sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
        }
+       if (sio_data->beep_pin)
+               pr_info("it87: Beeping is supported\n");
 
        /* Disable specific features based on DMI strings */
        board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
@@ -1093,14 +1548,46 @@ exit:
        return err;
 }
 
+static void it87_remove_files(struct device *dev)
+{
+       struct it87_data *data = platform_get_drvdata(pdev);
+       struct it87_sio_data *sio_data = dev->platform_data;
+       const struct attribute_group *fan_group = it87_get_fan_group(data);
+       int i;
+
+       sysfs_remove_group(&dev->kobj, &it87_group);
+       if (sio_data->beep_pin)
+               sysfs_remove_group(&dev->kobj, &it87_group_beep);
+       for (i = 0; i < 5; i++) {
+               if (!(data->has_fan & (1 << i)))
+                       continue;
+               sysfs_remove_group(&dev->kobj, &fan_group[i]);
+               if (sio_data->beep_pin)
+                       sysfs_remove_file(&dev->kobj,
+                                         it87_attributes_fan_beep[i]);
+       }
+       for (i = 0; i < 3; i++) {
+               if (sio_data->skip_pwm & (1 << 0))
+                       continue;
+               sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]);
+               if (has_old_autopwm(data))
+                       sysfs_remove_group(&dev->kobj,
+                                          &it87_group_autopwm[i]);
+       }
+       if (!sio_data->skip_vid)
+               sysfs_remove_group(&dev->kobj, &it87_group_vid);
+}
+
 static int __devinit it87_probe(struct platform_device *pdev)
 {
        struct it87_data *data;
        struct resource *res;
        struct device *dev = &pdev->dev;
        struct it87_sio_data *sio_data = dev->platform_data;
-       int err = 0;
+       const struct attribute_group *fan_group;
+       int err = 0, i;
        int enable_pwm_interface;
+       int fan_beep_need_rw;
        static const char *names[] = {
                "it87",
                "it8712",
@@ -1118,7 +1605,8 @@ static int __devinit it87_probe(struct platform_device *pdev)
                goto ERROR0;
        }
 
-       if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) {
+       data = kzalloc(sizeof(struct it87_data), GFP_KERNEL);
+       if (!data) {
                err = -ENOMEM;
                goto ERROR1;
        }
@@ -1146,120 +1634,60 @@ static int __devinit it87_probe(struct platform_device *pdev)
        it87_init_device(pdev);
 
        /* Register sysfs hooks */
-       if ((err = sysfs_create_group(&dev->kobj, &it87_group)))
+       err = sysfs_create_group(&dev->kobj, &it87_group);
+       if (err)
                goto ERROR2;
 
+       if (sio_data->beep_pin) {
+               err = sysfs_create_group(&dev->kobj, &it87_group_beep);
+               if (err)
+                       goto ERROR4;
+       }
+
        /* Do not create fan files for disabled fans */
-       if (has_16bit_fans(data)) {
-               /* 16-bit tachometers */
-               if (data->has_fan & (1 << 0)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_input16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_min16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-               if (data->has_fan & (1 << 1)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_input16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_min16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-               if (data->has_fan & (1 << 2)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_input16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_min16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-               if (data->has_fan & (1 << 3)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan4_input16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan4_min16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan4_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-               if (data->has_fan & (1 << 4)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan5_input16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan5_min16.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan5_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-       } else {
-               /* 8-bit tachometers with clock divider */
-               if (data->has_fan & (1 << 0)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_input.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_min.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_div.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan1_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-               if (data->has_fan & (1 << 1)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_input.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_min.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_div.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan2_alarm.dev_attr)))
-                               goto ERROR4;
-               }
-               if (data->has_fan & (1 << 2)) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_input.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_min.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_div.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_fan3_alarm.dev_attr)))
+       fan_group = it87_get_fan_group(data);
+       fan_beep_need_rw = 1;
+       for (i = 0; i < 5; i++) {
+               if (!(data->has_fan & (1 << i)))
+                       continue;
+               err = sysfs_create_group(&dev->kobj, &fan_group[i]);
+               if (err)
+                       goto ERROR4;
+
+               if (sio_data->beep_pin) {
+                       err = sysfs_create_file(&dev->kobj,
+                                               it87_attributes_fan_beep[i]);
+                       if (err)
                                goto ERROR4;
+                       if (!fan_beep_need_rw)
+                               continue;
+
+                       /* As we have a single beep enable bit for all fans,
+                        * only the first enabled fan has a writable attribute
+                        * for it. */
+                       if (sysfs_chmod_file(&dev->kobj,
+                                            it87_attributes_fan_beep[i],
+                                            S_IRUGO | S_IWUSR))
+                               dev_dbg(dev, "chmod +w fan%d_beep failed\n",
+                                       i + 1);
+                       fan_beep_need_rw = 0;
                }
        }
 
        if (enable_pwm_interface) {
-               if (!(sio_data->skip_pwm & (1 << 0))) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_pwm1_enable.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_pwm1.dev_attr))
-                        || (err = device_create_file(dev,
-                            &dev_attr_pwm1_freq)))
-                               goto ERROR4;
-               }
-               if (!(sio_data->skip_pwm & (1 << 1))) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_pwm2_enable.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_pwm2.dev_attr))
-                        || (err = device_create_file(dev,
-                            &dev_attr_pwm2_freq)))
+               for (i = 0; i < 3; i++) {
+                       if (sio_data->skip_pwm & (1 << i))
+                               continue;
+                       err = sysfs_create_group(&dev->kobj,
+                                                &it87_group_pwm[i]);
+                       if (err)
                                goto ERROR4;
-               }
-               if (!(sio_data->skip_pwm & (1 << 2))) {
-                       if ((err = device_create_file(dev,
-                            &sensor_dev_attr_pwm3_enable.dev_attr))
-                        || (err = device_create_file(dev,
-                            &sensor_dev_attr_pwm3.dev_attr))
-                        || (err = device_create_file(dev,
-                            &dev_attr_pwm3_freq)))
+
+                       if (!has_old_autopwm(data))
+                               continue;
+                       err = sysfs_create_group(&dev->kobj,
+                                                &it87_group_autopwm[i]);
+                       if (err)
                                goto ERROR4;
                }
        }
@@ -1268,10 +1696,8 @@ static int __devinit it87_probe(struct platform_device *pdev)
                data->vrm = vid_which_vrm();
                /* VID reading from Super-I/O config space if available */
                data->vid = sio_data->vid_value;
-               if ((err = device_create_file(dev,
-                    &dev_attr_vrm))
-                || (err = device_create_file(dev,
-                    &dev_attr_cpu0_vid)))
+               err = sysfs_create_group(&dev->kobj, &it87_group_vid);
+               if (err)
                        goto ERROR4;
        }
 
@@ -1284,8 +1710,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
        return 0;
 
 ERROR4:
-       sysfs_remove_group(&dev->kobj, &it87_group);
-       sysfs_remove_group(&dev->kobj, &it87_group_opt);
+       it87_remove_files(dev);
 ERROR2:
        platform_set_drvdata(pdev, NULL);
        kfree(data);
@@ -1300,8 +1725,7 @@ static int __devexit it87_remove(struct platform_device *pdev)
        struct it87_data *data = platform_get_drvdata(pdev);
 
        hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&pdev->dev.kobj, &it87_group);
-       sysfs_remove_group(&pdev->dev.kobj, &it87_group_opt);
+       it87_remove_files(&pdev->dev);
 
        release_region(data->addr, IT87_EC_EXTENT);
        platform_set_drvdata(pdev, NULL);
@@ -1387,15 +1811,18 @@ static void __devinit it87_init_device(struct platform_device *pdev)
        int tmp, i;
        u8 mask;
 
-       /* initialize to sane defaults:
-        * - if the chip is in manual pwm mode, this will be overwritten with
-        *   the actual settings on the chip (so in this case, initialization
-        *   is not needed)
-        * - if in automatic or on/off mode, we could switch to manual mode,
-        *   read the registers and set manual_pwm_ctl accordingly, but currently
-        *   this is not implemented, so we initialize to something sane */
+       /* For each PWM channel:
+        * - If it is in automatic mode, setting to manual mode should set
+        *   the fan to full speed by default.
+        * - If it is in manual mode, we need a mapping to temperature
+        *   channels to use when later setting to automatic mode later.
+        *   Use a 1:1 mapping by default (we are clueless.)
+        * In both cases, the value can (and should) be changed by the user
+        * prior to switching to a different mode. */
        for (i = 0; i < 3; i++) {
-               data->manual_pwm_ctl[i] = 0xff;
+               data->pwm_temp_map[i] = i;
+               data->pwm_duty[i] = 0x7f;       /* Full speed */
+               data->auto_pwm[i][3] = 0x7f;    /* Full speed, hard-coded */
        }
 
        /* Some chips seem to have default value 0xff for all limit
@@ -1436,7 +1863,8 @@ static void __devinit it87_init_device(struct platform_device *pdev)
        if ((data->fan_main_ctrl & mask) == 0) {
                /* Enable all fan tachometers */
                data->fan_main_ctrl |= mask;
-               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
+               it87_write_value(data, IT87_REG_FAN_MAIN_CTRL,
+                                data->fan_main_ctrl);
        }
        data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
 
@@ -1461,30 +1889,32 @@ static void __devinit it87_init_device(struct platform_device *pdev)
        /* Fan input pins may be used for alternative functions */
        data->has_fan &= ~sio_data->skip_fan;
 
-       /* Set current fan mode registers and the default settings for the
-        * other mode registers */
-       for (i = 0; i < 3; i++) {
-               if (data->fan_main_ctrl & (1 << i)) {
-                       /* pwm mode */
-                       tmp = it87_read_value(data, IT87_REG_PWM(i));
-                       if (tmp & 0x80) {
-                               /* automatic pwm - not yet implemented, but
-                                * leave the settings made by the BIOS alone
-                                * until a change is requested via the sysfs
-                                * interface */
-                       } else {
-                               /* manual pwm */
-                               data->manual_pwm_ctl[i] = PWM_FROM_REG(tmp);
-                       }
-               }
-       }
-
        /* Start monitoring */
        it87_write_value(data, IT87_REG_CONFIG,
                         (it87_read_value(data, IT87_REG_CONFIG) & 0x36)
                         | (update_vbat ? 0x41 : 0x01));
 }
 
+static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
+{
+       data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM(nr));
+       if (data->pwm_ctrl[nr] & 0x80)  /* Automatic mode */
+               data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03;
+       else                            /* Manual mode */
+               data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f;
+
+       if (has_old_autopwm(data)) {
+               int i;
+
+               for (i = 0; i < 5 ; i++)
+                       data->auto_temp[nr][i] = it87_read_value(data,
+                                               IT87_REG_AUTO_TEMP(nr, i));
+               for (i = 0; i < 3 ; i++)
+                       data->auto_pwm[nr][i] = it87_read_value(data,
+                                               IT87_REG_AUTO_PWM(nr, i));
+       }
+}
+
 static struct it87_data *it87_update_device(struct device *dev)
 {
        struct it87_data *data = dev_get_drvdata(dev);
@@ -1494,24 +1924,22 @@ static struct it87_data *it87_update_device(struct device *dev)
 
        if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
            || !data->valid) {
-
                if (update_vbat) {
                        /* Cleared after each update, so reenable.  Value
-                         returned by this read will be previous value */       
+                          returned by this read will be previous value */
                        it87_write_value(data, IT87_REG_CONFIG,
-                          it87_read_value(data, IT87_REG_CONFIG) | 0x40);
+                               it87_read_value(data, IT87_REG_CONFIG) | 0x40);
                }
                for (i = 0; i <= 7; i++) {
                        data->in[i] =
-                           it87_read_value(data, IT87_REG_VIN(i));
+                               it87_read_value(data, IT87_REG_VIN(i));
                        data->in_min[i] =
-                           it87_read_value(data, IT87_REG_VIN_MIN(i));
+                               it87_read_value(data, IT87_REG_VIN_MIN(i));
                        data->in_max[i] =
-                           it87_read_value(data, IT87_REG_VIN_MAX(i));
+                               it87_read_value(data, IT87_REG_VIN_MAX(i));
                }
                /* in8 (battery) has no limit registers */
-               data->in[8] =
-                   it87_read_value(data, IT87_REG_VIN(8));
+               data->in[8] = it87_read_value(data, IT87_REG_VIN(8));
 
                for (i = 0; i < 5; i++) {
                        /* Skip disabled fans */
@@ -1519,7 +1947,7 @@ static struct it87_data *it87_update_device(struct device *dev)
                                continue;
 
                        data->fan_min[i] =
-                           it87_read_value(data, IT87_REG_FAN_MIN[i]);
+                               it87_read_value(data, IT87_REG_FAN_MIN[i]);
                        data->fan[i] = it87_read_value(data,
                                       IT87_REG_FAN[i]);
                        /* Add high byte if in 16-bit mode */
@@ -1532,11 +1960,11 @@ static struct it87_data *it87_update_device(struct device *dev)
                }
                for (i = 0; i < 3; i++) {
                        data->temp[i] =
-                           it87_read_value(data, IT87_REG_TEMP(i));
+                               it87_read_value(data, IT87_REG_TEMP(i));
                        data->temp_high[i] =
-                           it87_read_value(data, IT87_REG_TEMP_HIGH(i));
+                               it87_read_value(data, IT87_REG_TEMP_HIGH(i));
                        data->temp_low[i] =
-                           it87_read_value(data, IT87_REG_TEMP_LOW(i));
+                               it87_read_value(data, IT87_REG_TEMP_LOW(i));
                }
 
                /* Newer chips don't have clock dividers */
@@ -1551,9 +1979,13 @@ static struct it87_data *it87_update_device(struct device *dev)
                        it87_read_value(data, IT87_REG_ALARM1) |
                        (it87_read_value(data, IT87_REG_ALARM2) << 8) |
                        (it87_read_value(data, IT87_REG_ALARM3) << 16);
+               data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE);
+
                data->fan_main_ctrl = it87_read_value(data,
                                IT87_REG_FAN_MAIN_CTRL);
                data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL);
+               for (i = 0; i < 3; i++)
+                       it87_update_pwm_ctrl(data, i);
 
                data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE);
                /* The 8705 does not have VID capability.
@@ -1628,7 +2060,7 @@ exit:
 static int __init sm_it87_init(void)
 {
        int err;
-       unsigned short isa_address=0;
+       unsigned short isa_address = 0;
        struct it87_sio_data sio_data;
 
        memset(&sio_data, 0, sizeof(struct it87_sio_data));
@@ -1640,7 +2072,7 @@ static int __init sm_it87_init(void)
                return err;
 
        err = it87_device_add(isa_address, &sio_data);
-       if (err){
+       if (err) {
                platform_driver_unregister(&it87_driver);
                return err;
        }
@@ -1661,7 +2093,8 @@ MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver");
 module_param(update_vbat, bool, 0);
 MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
 module_param(fix_pwm_polarity, bool, 0);
-MODULE_PARM_DESC(fix_pwm_polarity, "Force PWM polarity to active high (DANGEROUS)");
+MODULE_PARM_DESC(fix_pwm_polarity,
+                "Force PWM polarity to active high (DANGEROUS)");
 MODULE_LICENSE("GPL");
 
 module_init(sm_it87_init);
index 7c9bdc16742653e0dd2bcb812542f7c1f61a233d..7cc2708871abb7784afda170bd839517604ccb2d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
  *          monitoring
- * Copyright (C) 2003-2009  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2010  Jean Delvare <khali@linux-fr.org>
  *
  * Based on the lm83 driver. The LM90 is a sensor chip made by National
  * Semiconductor. It reports up to two temperatures (its own plus up to
@@ -93,7 +93,8 @@
 static const unsigned short normal_i2c[] = {
        0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 };
+enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646,
+       w83l771 };
 
 /*
  * The LM90 registers
@@ -151,6 +152,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info);
 static int lm90_probe(struct i2c_client *client,
                      const struct i2c_device_id *id);
 static void lm90_init_client(struct i2c_client *client);
+static void lm90_alert(struct i2c_client *client, unsigned int flag);
 static int lm90_remove(struct i2c_client *client);
 static struct lm90_data *lm90_update_device(struct device *dev);
 
@@ -173,6 +175,7 @@ static const struct i2c_device_id lm90_id[] = {
        { "max6659", max6657 },
        { "max6680", max6680 },
        { "max6681", max6680 },
+       { "w83l771", w83l771 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm90_id);
@@ -184,6 +187,7 @@ static struct i2c_driver lm90_driver = {
        },
        .probe          = lm90_probe,
        .remove         = lm90_remove,
+       .alert          = lm90_alert,
        .id_table       = lm90_id,
        .detect         = lm90_detect,
        .address_list   = normal_i2c,
@@ -201,6 +205,9 @@ struct lm90_data {
        int kind;
        int flags;
 
+       u8 config_orig;         /* Original configuration register value */
+       u8 alert_alarms;        /* Which alarm bits trigger ALERT# */
+
        /* registers values */
        s8 temp8[4];    /* 0: local low limit
                           1: local high limit
@@ -758,6 +765,14 @@ static int lm90_detect(struct i2c_client *new_client,
                 && reg_convrate <= 0x07) {
                        name = "max6646";
                }
+       } else
+       if (address == 0x4C
+        && man_id == 0x5C) { /* Winbond/Nuvoton */
+               if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */
+                && (reg_config1 & 0x2A) == 0x00
+                && reg_convrate <= 0x08) {
+                       name = "w83l771";
+               }
        }
 
        if (!name) { /* identification failed */
@@ -794,6 +809,19 @@ static int lm90_probe(struct i2c_client *new_client,
                        new_client->flags &= ~I2C_CLIENT_PEC;
        }
 
+       /* Different devices have different alarm bits triggering the
+        * ALERT# output */
+       switch (data->kind) {
+       case lm90:
+       case lm99:
+       case lm86:
+               data->alert_alarms = 0x7b;
+               break;
+       default:
+               data->alert_alarms = 0x7c;
+               break;
+       }
+
        /* Initialize the LM90 chip */
        lm90_init_client(new_client);
 
@@ -830,7 +858,7 @@ exit:
 
 static void lm90_init_client(struct i2c_client *client)
 {
-       u8 config, config_orig;
+       u8 config;
        struct lm90_data *data = i2c_get_clientdata(client);
 
        /*
@@ -842,7 +870,7 @@ static void lm90_init_client(struct i2c_client *client)
                dev_warn(&client->dev, "Initialization failed!\n");
                return;
        }
-       config_orig = config;
+       data->config_orig = config;
 
        /* Check Temperature Range Select */
        if (data->kind == adt7461) {
@@ -860,7 +888,7 @@ static void lm90_init_client(struct i2c_client *client)
        }
 
        config &= 0xBF; /* run */
-       if (config != config_orig) /* Only write if changed */
+       if (config != data->config_orig) /* Only write if changed */
                i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config);
 }
 
@@ -875,10 +903,46 @@ static int lm90_remove(struct i2c_client *client)
                device_remove_file(&client->dev,
                                   &sensor_dev_attr_temp2_offset.dev_attr);
 
+       /* Restore initial configuration */
+       i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
+                                 data->config_orig);
+
        kfree(data);
        return 0;
 }
 
+static void lm90_alert(struct i2c_client *client, unsigned int flag)
+{
+       struct lm90_data *data = i2c_get_clientdata(client);
+       u8 config, alarms;
+
+       lm90_read_reg(client, LM90_REG_R_STATUS, &alarms);
+       if ((alarms & 0x7f) == 0) {
+               dev_info(&client->dev, "Everything OK\n");
+       } else {
+               if (alarms & 0x61)
+                       dev_warn(&client->dev,
+                                "temp%d out of range, please check!\n", 1);
+               if (alarms & 0x1a)
+                       dev_warn(&client->dev,
+                                "temp%d out of range, please check!\n", 2);
+               if (alarms & 0x04)
+                       dev_warn(&client->dev,
+                                "temp%d diode open, please check!\n", 2);
+
+               /* Disable ALERT# output, because these chips don't implement
+                 SMBus alert correctly; they should only hold the alert line
+                 low briefly. */
+               if ((data->kind == adm1032 || data->kind == adt7461)
+                && (alarms & data->alert_alarms)) {
+                       dev_dbg(&client->dev, "Disabling ALERT#\n");
+                       lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
+                       i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
+                                                 config | 0x80);
+               }
+       }
+}
+
 static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value)
 {
        int err;
@@ -966,6 +1030,21 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                }
                lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms);
 
+               /* Re-enable ALERT# output if it was originally enabled and
+                * relevant alarms are all clear */
+               if ((data->config_orig & 0x80) == 0
+                && (data->alarms & data->alert_alarms) == 0) {
+                       u8 config;
+
+                       lm90_read_reg(client, LM90_REG_R_CONFIG1, &config);
+                       if (config & 0x80) {
+                               dev_dbg(&client->dev, "Re-enabling ALERT#\n");
+                               i2c_smbus_write_byte_data(client,
+                                                         LM90_REG_W_CONFIG1,
+                                                         config & ~0x80);
+                       }
+               }
+
                data->last_updated = jiffies;
                data->valid = 1;
        }
index a13b30e8d8d8f982f71ff5e22f42f1347e7f55ad..d14a1af9f550ae86eeffc5f585129921f41b84e2 100644 (file)
@@ -134,7 +134,7 @@ struct tmp401_data {
        struct mutex update_lock;
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
-       int kind;
+       enum chips kind;
 
        /* register values */
        u8 status;
@@ -524,7 +524,7 @@ static int tmp401_detect(struct i2c_client *client,
        if (reg > 15)
                return -ENODEV;
 
-       strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE);
+       strlcpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE);
 
        return 0;
 }
@@ -572,8 +572,7 @@ static int tmp401_probe(struct i2c_client *client,
                goto exit_remove;
        }
 
-       dev_info(&client->dev, "Detected TI %s chip\n",
-                names[data->kind - 1]);
+       dev_info(&client->dev, "Detected TI %s chip\n", names[data->kind]);
 
        return 0;
 
index 4f7c051e2d7b0425308489fcbbc6c04eda68947d..738c472ece273bf972b1206e9b164b88d046b0bc 100644 (file)
@@ -61,9 +61,9 @@ static const u8 TMP421_TEMP_LSB[4]            = { 0x10, 0x11, 0x12, 0x13 };
 #define TMP423_DEVICE_ID                       0x23
 
 static const struct i2c_device_id tmp421_id[] = {
-       { "tmp421", tmp421 },
-       { "tmp422", tmp422 },
-       { "tmp423", tmp423 },
+       { "tmp421", 2 },
+       { "tmp422", 3 },
+       { "tmp423", 4 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, tmp421_id);
@@ -73,21 +73,23 @@ struct tmp421_data {
        struct mutex update_lock;
        char valid;
        unsigned long last_updated;
-       int kind;
+       int channels;
        u8 config;
        s16 temp[4];
 };
 
 static int temp_from_s16(s16 reg)
 {
-       int temp = reg;
+       /* Mask out status bits */
+       int temp = reg & ~0xf;
 
        return (temp * 1000 + 128) / 256;
 }
 
 static int temp_from_u16(u16 reg)
 {
-       int temp = reg;
+       /* Mask out status bits */
+       int temp = reg & ~0xf;
 
        /* Add offset for extended temperature range. */
        temp -= 64 * 256;
@@ -107,7 +109,7 @@ static struct tmp421_data *tmp421_update_device(struct device *dev)
                data->config = i2c_smbus_read_byte_data(client,
                        TMP421_CONFIG_REG_1);
 
-               for (i = 0; i <= data->kind; i++) {
+               for (i = 0; i < data->channels; i++) {
                        data->temp[i] = i2c_smbus_read_byte_data(client,
                                TMP421_TEMP_MSB[i]) << 8;
                        data->temp[i] |= i2c_smbus_read_byte_data(client,
@@ -166,7 +168,7 @@ static mode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a,
        devattr = container_of(a, struct device_attribute, attr);
        index = to_sensor_dev_attr(devattr)->index;
 
-       if (data->kind > index)
+       if (index < data->channels)
                return a->mode;
 
        return 0;
@@ -252,9 +254,9 @@ static int tmp421_detect(struct i2c_client *client,
                return -ENODEV;
        }
 
-       strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE);
+       strlcpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE);
        dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n",
-                names[kind - 1], client->addr);
+                names[kind], client->addr);
 
        return 0;
 }
@@ -271,7 +273,7 @@ static int tmp421_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, data);
        mutex_init(&data->update_lock);
-       data->kind = id->driver_data;
+       data->channels = id->driver_data;
 
        err = tmp421_init_client(client);
        if (err)
index d47b4c9949c2f7da720e2c69986193e2f76c96e6..e6078c9f0e279d33e62df726c7feaace7e8fa36d 100644 (file)
@@ -948,8 +948,7 @@ static int __devinit vt8231_pci_probe(struct pci_dev *dev,
 
        address = val & ~(VT8231_EXTENT - 1);
        if (address == 0) {
-               dev_err(&dev->dev, "base address not set -\
-                                upgrade BIOS or use force_addr=0xaddr\n");
+               dev_err(&dev->dev, "base address not set - upgrade BIOS or use force_addr=0xaddr\n");
                return -ENODEV;
        }
 
index 9a2022b67495d9d2daa019173dae42f66c5b24ce..9de81a4c15a2127b774766bda5244bf519d750cf 100644 (file)
@@ -3,6 +3,10 @@
     Copyright (C) 2006 Winbond Electronics Corp.
                   Yuan Mu
                   Rudolf Marek <r.marek@assembler.cz>
+    Copyright (C) 2009-2010 Sven Anders <anders@anduras.de>, ANDURAS AG.
+                 Watchdog driver part
+                 (Based partially on fschmd driver,
+                  Copyright 2007-2008 by Hans de Goede)
 
     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
 #include <linux/hwmon-sysfs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
+#include <linux/fs.h>
+#include <linux/watchdog.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kref.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+/* Default values */
+#define WATCHDOG_TIMEOUT 2     /* 2 minute default timeout */
 
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
@@ -51,6 +65,18 @@ static int reset;
 module_param(reset, bool, 0);
 MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
 
+static int timeout = WATCHDOG_TIMEOUT; /* default timeout in minutes */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+       "Watchdog timeout in minutes. 2<= timeout <=255 (default="
+                               __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+       "Watchdog cannot be stopped once started (default="
+                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
 /*
    Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved
    as ID, Bank Select registers
@@ -72,6 +98,11 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
 #define W83793_REG_VID_LATCHB          0x08
 #define W83793_REG_VID_CTRL            0x59
 
+#define W83793_REG_WDT_LOCK            0x01
+#define W83793_REG_WDT_ENABLE          0x02
+#define W83793_REG_WDT_STATUS          0x03
+#define W83793_REG_WDT_TIMEOUT         0x04
+
 static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f };
 
 #define TEMP_READ      0
@@ -223,8 +254,37 @@ struct w83793_data {
        u8 tolerance[3];        /* Temp tolerance(Smart Fan I/II) */
        u8 sf2_pwm[6][7];       /* Smart FanII: Fan duty cycle */
        u8 sf2_temp[6][7];      /* Smart FanII: Temp level point */
+
+       /* watchdog */
+       struct i2c_client *client;
+       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_expect_close;
+       char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
+       unsigned int watchdog_caused_reboot;
+       int watchdog_timeout; /* watchdog timeout in minutes */
 };
 
+/* Somewhat ugly :( global data pointer list with all devices, so that
+   we can find our device data as when using misc_register. There is no
+   other method to get to one's device data from the open file-op and
+   for usage in the reboot notifier callback. */
+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);
+
+/* Release our data struct when we're detached from the i2c client *and* all
+   references to our watchdog device are released */
+static void w83793_release_resources(struct kref *ref)
+{
+       struct w83793_data *data = container_of(ref, struct w83793_data, kref);
+       kfree(data);
+}
+
 static u8 w83793_read_value(struct i2c_client *client, u16 reg);
 static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value);
 static int w83793_probe(struct i2c_client *client,
@@ -1063,14 +1123,349 @@ static void w83793_init_client(struct i2c_client *client)
        /* Start monitoring */
        w83793_write_value(client, W83793_REG_CONFIG,
                           w83793_read_value(client, W83793_REG_CONFIG) | 0x01);
+}
+
+/*
+ * Watchdog routines
+ */
+
+static int watchdog_set_timeout(struct w83793_data *data, int timeout)
+{
+       int ret, mtimeout;
+
+       mtimeout = DIV_ROUND_UP(timeout, 60);
+
+       if (mtimeout > 255)
+               return -EINVAL;
+
+       mutex_lock(&data->watchdog_lock);
+       if (!data->client) {
+               ret = -ENODEV;
+               goto leave;
+       }
+
+       data->watchdog_timeout = mtimeout;
+
+       /* Set Timeout value (in Minutes) */
+       w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT,
+                          data->watchdog_timeout);
+
+       ret = mtimeout * 60;
+
+leave:
+       mutex_unlock(&data->watchdog_lock);
+       return ret;
+}
+
+static int watchdog_get_timeout(struct w83793_data *data)
+{
+       int timeout;
+
+       mutex_lock(&data->watchdog_lock);
+       timeout = data->watchdog_timeout * 60;
+       mutex_unlock(&data->watchdog_lock);
+
+       return timeout;
+}
+
+static int watchdog_trigger(struct w83793_data *data)
+{
+       int ret = 0;
+
+       mutex_lock(&data->watchdog_lock);
+       if (!data->client) {
+               ret = -ENODEV;
+               goto leave;
+       }
+
+       /* Set Timeout value (in Minutes) */
+       w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT,
+                          data->watchdog_timeout);
+
+leave:
+       mutex_unlock(&data->watchdog_lock);
+       return ret;
+}
+
+static int watchdog_enable(struct w83793_data *data)
+{
+       int ret = 0;
+
+       mutex_lock(&data->watchdog_lock);
+       if (!data->client) {
+               ret = -ENODEV;
+               goto leave;
+       }
+
+       /* Set initial timeout */
+       w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT,
+                          data->watchdog_timeout);
+
+       /* Enable Soft Watchdog */
+       w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0x55);
+
+leave:
+       mutex_unlock(&data->watchdog_lock);
+       return ret;
+}
+
+static int watchdog_disable(struct w83793_data *data)
+{
+       int ret = 0;
+
+       mutex_lock(&data->watchdog_lock);
+       if (!data->client) {
+               ret = -ENODEV;
+               goto leave;
+       }
+
+       /* Disable Soft Watchdog */
+       w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0xAA);
+
+leave:
+       mutex_unlock(&data->watchdog_lock);
+       return ret;
+}
+
+static int watchdog_open(struct inode *inode, struct file *filp)
+{
+       struct w83793_data *pos, *data = NULL;
+       int watchdog_is_open;
+
+       /* We get called from drivers/char/misc.c with misc_mtx hold, and we
+          call misc_register() from  w83793_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;
+               }
+       }
+
+       /* Check, if device is already open */
+       watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open);
+
+       /* Increase data reference counter (if not already done).
+          Note we can never not have found data, so we don't check for this */
+       if (!watchdog_is_open)
+               kref_get(&data->kref);
+
+       mutex_unlock(&watchdog_data_mutex);
+
+       /* Check, if device is already open and possibly issue error */
+       if (watchdog_is_open)
+               return -EBUSY;
+
+       /* Enable Soft Watchdog */
+       watchdog_enable(data);
+
+       /* Store pointer to data into filp's private data */
+       filp->private_data = data;
+
+       return nonseekable_open(inode, filp);
+}
+
+static int watchdog_close(struct inode *inode, struct file *filp)
+{
+       struct w83793_data *data = filp->private_data;
 
+       if (data->watchdog_expect_close) {
+               watchdog_disable(data);
+               data->watchdog_expect_close = 0;
+       } else {
+               watchdog_trigger(data);
+               dev_crit(&data->client->dev,
+                       "unexpected close, not stopping watchdog!\n");
+       }
+
+       clear_bit(0, &data->watchdog_is_open);
+
+       /* Decrease data reference counter */
+       mutex_lock(&watchdog_data_mutex);
+       kref_put(&data->kref, w83793_release_resources);
+       mutex_unlock(&watchdog_data_mutex);
+
+       return 0;
+}
+
+static ssize_t watchdog_write(struct file *filp, const char __user *buf,
+       size_t count, loff_t *offset)
+{
+       size_t ret;
+       struct w83793_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 < 0)
+                       return ret;
+       }
+       return count;
+}
+
+static int watchdog_ioctl(struct inode *inode, struct file *filp,
+                         unsigned int cmd, unsigned long arg)
+{
+       static struct watchdog_info ident = {
+               .options = WDIOF_KEEPALIVEPING |
+                          WDIOF_SETTIMEOUT |
+                          WDIOF_CARDRESET,
+               .identity = "w83793 watchdog"
+       };
+
+       int val, ret = 0;
+       struct w83793_data *data = filp->private_data;
+
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               if (!nowayout)
+                       ident.options |= WDIOF_MAGICCLOSE;
+               if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
+                       ret = -EFAULT;
+               break;
+
+       case WDIOC_GETSTATUS:
+               val = data->watchdog_caused_reboot ? WDIOF_CARDRESET : 0;
+               ret = put_user(val, (int __user *)arg);
+               break;
+
+       case WDIOC_GETBOOTSTATUS:
+               ret = put_user(0, (int __user *)arg);
+               break;
+
+       case WDIOC_KEEPALIVE:
+               ret = watchdog_trigger(data);
+               break;
+
+       case WDIOC_GETTIMEOUT:
+               val = watchdog_get_timeout(data);
+               ret = put_user(val, (int __user *)arg);
+               break;
+
+       case WDIOC_SETTIMEOUT:
+               if (get_user(val, (int __user *)arg)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               ret = watchdog_set_timeout(data, val);
+               if (ret > 0)
+                       ret = put_user(ret, (int __user *)arg);
+               break;
+
+       case WDIOC_SETOPTIONS:
+               if (get_user(val, (int __user *)arg)) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               if (val & WDIOS_DISABLECARD)
+                       ret = watchdog_disable(data);
+               else if (val & WDIOS_ENABLECARD)
+                       ret = watchdog_enable(data);
+               else
+                       ret = -EINVAL;
+
+               break;
+       default:
+               ret = -ENOTTY;
+       }
+
+       return ret;
+}
+
+static const struct file_operations watchdog_fops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = watchdog_open,
+       .release = watchdog_close,
+       .write = watchdog_write,
+       .ioctl = watchdog_ioctl,
+};
+
+/*
+ *     Notifier for system down
+ */
+
+static int watchdog_notify_sys(struct notifier_block *this, unsigned long code,
+                              void *unused)
+{
+       struct w83793_data *data = NULL;
+
+       if (code == SYS_DOWN || code == SYS_HALT) {
+
+               /* Disable each registered watchdog */
+               mutex_lock(&watchdog_data_mutex);
+               list_for_each_entry(data, &watchdog_data_list, list) {
+                       if (data->watchdog_miscdev.minor)
+                               watchdog_disable(data);
+               }
+               mutex_unlock(&watchdog_data_mutex);
+       }
+
+       return NOTIFY_DONE;
 }
 
+/*
+ *     The WDT needs to learn about soft shutdowns in order to
+ *     turn the timebomb registers off.
+ */
+
+static struct notifier_block watchdog_notifier = {
+       .notifier_call = watchdog_notify_sys,
+};
+
+/*
+ * Init / remove routines
+ */
+
 static int w83793_remove(struct i2c_client *client)
 {
        struct w83793_data *data = i2c_get_clientdata(client);
        struct device *dev = &client->dev;
-       int i;
+       int i, tmp;
+
+       /* Unregister the watchdog (if registered) */
+       if (data->watchdog_miscdev.minor) {
+               misc_deregister(&data->watchdog_miscdev);
+
+               if (data->watchdog_is_open) {
+                       dev_warn(&client->dev,
+                               "i2c client detached with watchdog open! "
+                               "Stopping watchdog.\n");
+                       watchdog_disable(data);
+               }
+
+               mutex_lock(&watchdog_data_mutex);
+               list_del(&data->list);
+               mutex_unlock(&watchdog_data_mutex);
+
+               /* Tell the watchdog code the client is gone */
+               mutex_lock(&data->watchdog_lock);
+               data->client = NULL;
+               mutex_unlock(&data->watchdog_lock);
+       }
+
+       /* Reset Configuration Register to Disable Watch Dog Registers */
+       tmp = w83793_read_value(client, W83793_REG_CONFIG);
+       w83793_write_value(client, W83793_REG_CONFIG, tmp & ~0x04);
+
+       unregister_reboot_notifier(&watchdog_notifier);
 
        hwmon_device_unregister(data->hwmon_dev);
 
@@ -1099,7 +1494,10 @@ static int w83793_remove(struct i2c_client *client)
        if (data->lm75[1] != NULL)
                i2c_unregister_device(data->lm75[1]);
 
-       kfree(data);
+       /* Decrease data reference counter */
+       mutex_lock(&watchdog_data_mutex);
+       kref_put(&data->kref, w83793_release_resources);
+       mutex_unlock(&watchdog_data_mutex);
 
        return 0;
 }
@@ -1203,6 +1601,7 @@ static int w83793_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
+       const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
        struct w83793_data *data;
        int i, tmp, val, err;
        int files_fan = ARRAY_SIZE(w83793_left_fan) / 7;
@@ -1218,6 +1617,14 @@ static int w83793_probe(struct i2c_client *client,
        i2c_set_clientdata(client, data);
        data->bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL);
        mutex_init(&data->update_lock);
+       mutex_init(&data->watchdog_lock);
+       INIT_LIST_HEAD(&data->list);
+       kref_init(&data->kref);
+
+       /* Store client pointer in our data struct for watchdog usage
+          (where the client is found through a data ptr instead of the
+          otherway around) */
+       data->client = client;
 
        err = w83793_detect_subclients(client);
        if (err)
@@ -1380,8 +1787,77 @@ static int w83793_probe(struct i2c_client *client,
                goto exit_remove;
        }
 
+       /* Watchdog initialization */
+
+       /* Register boot notifier */
+       err = register_reboot_notifier(&watchdog_notifier);
+       if (err != 0) {
+               dev_err(&client->dev,
+                       "cannot register reboot notifier (err=%d)\n", err);
+               goto exit_devunreg;
+       }
+
+       /* Enable Watchdog registers.
+          Set Configuration Register to Enable Watch Dog Registers
+          (Bit 2) = XXXX, X1XX. */
+       tmp = w83793_read_value(client, W83793_REG_CONFIG);
+       w83793_write_value(client, W83793_REG_CONFIG, tmp | 0x04);
+
+       /* Set the default watchdog timeout */
+       data->watchdog_timeout = timeout;
+
+       /* Check, if last reboot was caused by watchdog */
+       data->watchdog_caused_reboot =
+         w83793_read_value(data->client, W83793_REG_WDT_STATUS) & 0x01;
+
+       /* Disable Soft Watchdog during initialiation */
+       watchdog_disable(data);
+
+       /* 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 (and set the default timeout) */
+       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) {
+                       data->watchdog_miscdev.minor = 0;
+                       dev_err(&client->dev,
+                               "Registering watchdog chardev: %d\n", err);
+                       break;
+               }
+
+               list_add(&data->list, &watchdog_data_list);
+
+               dev_info(&client->dev,
+                       "Registered watchdog chardev major 10, minor: %d\n",
+                       watchdog_minors[i]);
+               break;
+       }
+       if (i == ARRAY_SIZE(watchdog_minors)) {
+               data->watchdog_miscdev.minor = 0;
+               dev_warn(&client->dev, "Couldn't register watchdog chardev "
+                       "(due to no free minor)\n");
+       }
+
+       mutex_unlock(&watchdog_data_mutex);
+
        return 0;
 
+       /* Unregister hwmon device */
+
+exit_devunreg:
+
+       hwmon_device_unregister(data->hwmon_dev);
+
        /* Unregister sysfs hooks */
 
 exit_remove:
@@ -1628,7 +2104,7 @@ static void __exit sensors_w83793_exit(void)
        i2c_del_driver(&w83793_driver);
 }
 
-MODULE_AUTHOR("Yuan Mu");
+MODULE_AUTHOR("Yuan Mu, Sven Anders");
 MODULE_DESCRIPTION("w83793 driver");
 MODULE_LICENSE("GPL");
 
index 9e18ef97f1568981401cf752080d72c463c60076..3e72b69aa7f8a39ac8dc752a45048935d33d78b5 100644 (file)
@@ -497,13 +497,13 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
        int i;
 
        if (abort_source & DW_IC_TX_ABRT_NOACK) {
-               for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+               for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
                        dev_dbg(dev->dev,
                                "%s: %s\n", __func__, abort_sources[i]);
                return -EREMOTEIO;
        }
 
-       for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+       for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
                dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
 
        if (abort_source & DW_IC_TX_ARB_LOST)
index 878f8ec6dbe1147b182fdedca240b86041f37710..57d00caefc86c2762b1c5eeee5c3bd8e9a408b76 100644 (file)
@@ -81,15 +81,15 @@ static u8 pci_bus_clock_list_ultra (u8 speed, struct chipset_bus_clock_list_entr
        return chipset_table->ultra_settings;
 }
 
-static void aec6210_set_mode(ide_drive_t *drive, const u8 speed)
+static void aec6210_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        struct ide_host *host   = pci_get_drvdata(dev);
        struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
        u16 d_conf              = 0;
        u8 ultra = 0, ultra_conf = 0;
        u8 tmp0 = 0, tmp1 = 0, tmp2 = 0;
+       const u8 speed = drive->dma_mode;
        unsigned long flags;
 
        local_irq_save(flags);
@@ -109,15 +109,15 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed)
        local_irq_restore(flags);
 }
 
-static void aec6260_set_mode(ide_drive_t *drive, const u8 speed)
+static void aec6260_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        struct ide_host *host   = pci_get_drvdata(dev);
        struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
        u8 unit                 = drive->dn & 1;
        u8 tmp1 = 0, tmp2 = 0;
        u8 ultra = 0, drive_conf = 0, ultra_conf = 0;
+       const u8 speed = drive->dma_mode;
        unsigned long flags;
 
        local_irq_save(flags);
@@ -134,9 +134,10 @@ static void aec6260_set_mode(ide_drive_t *drive, const u8 speed)
        local_irq_restore(flags);
 }
 
-static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void aec_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0);
+       drive->dma_mode = drive->pio_mode;
+       hwif->port_ops->set_dma_mode(hwif, drive);
 }
 
 static int init_chipset_aec62xx(struct pci_dev *dev)
index 90da1f953ed080d0b0fb3ce38ff527398c42d00c..25b9fe3a9f8ef02047f689645755b347223a94a5 100644 (file)
@@ -109,13 +109,14 @@ static DEFINE_SPINLOCK(ali14xx_lock);
  * This function computes timing parameters
  * and sets controller registers accordingly.
  */
-static void ali14xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void ali14xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        int driveNum;
        int time1, time2;
        u8 param1, param2, param3, param4;
        unsigned long flags;
        int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
 
        /* calculate timing, according to PIO mode */
index 0abc43f3101e91eddeb7ba14e05e35b448e71e1a..2c8016ad0e269edf7887d13fc775ce4f9ebd1927 100644 (file)
@@ -8,7 +8,7 @@
  *  Copyright (C) 2002 Alan Cox
  *  ALi (now ULi M5228) support by Clear Zhang <Clear.Zhang@ali.com.tw>
  *  Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com>
- *  Copyright (C) 2007 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
+ *  Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz
  *
  *  (U)DMA capable version of ali 1533/1543(C), 1535(D)
  *
@@ -48,61 +48,84 @@ static u8 m5229_revision;
 static u8 chip_is_1543c_e;
 static struct pci_dev *isa_dev;
 
+static void ali_fifo_control(ide_hwif_t *hwif, ide_drive_t *drive, int on)
+{
+       struct pci_dev *pdev = to_pci_dev(hwif->dev);
+       int pio_fifo = 0x54 + hwif->channel;
+       u8 fifo;
+       int shift = 4 * (drive->dn & 1);
+
+       pci_read_config_byte(pdev, pio_fifo, &fifo);
+       fifo &= ~(0x0F << shift);
+       fifo |= (on << shift);
+       pci_write_config_byte(pdev, pio_fifo, fifo);
+}
+
+static void ali_program_timings(ide_hwif_t *hwif, ide_drive_t *drive,
+                               struct ide_timing *t, u8 ultra)
+{
+       struct pci_dev *dev = to_pci_dev(hwif->dev);
+       int port = hwif->channel ? 0x5c : 0x58;
+       int udmat = 0x56 + hwif->channel;
+       u8 unit = drive->dn & 1, udma;
+       int shift = 4 * unit;
+
+       /* Set up the UDMA */
+       pci_read_config_byte(dev, udmat, &udma);
+       udma &= ~(0x0F << shift);
+       udma |= ultra << shift;
+       pci_write_config_byte(dev, udmat, udma);
+
+       if (t == NULL)
+               return;
+
+       t->setup = clamp_val(t->setup, 1, 8) & 7;
+       t->act8b = clamp_val(t->act8b, 1, 8) & 7;
+       t->rec8b = clamp_val(t->rec8b, 1, 16) & 15;
+       t->active = clamp_val(t->active, 1, 8) & 7;
+       t->recover = clamp_val(t->recover, 1, 16) & 15;
+
+       pci_write_config_byte(dev, port, t->setup);
+       pci_write_config_byte(dev, port + 1, (t->act8b << 4) | t->rec8b);
+       pci_write_config_byte(dev, port + unit + 2,
+                             (t->active << 4) | t->recover);
+}
+
 /**
  *     ali_set_pio_mode        -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Program the controller for the given PIO mode.
  */
 
-static void ali_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void ali_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
-       struct pci_dev *dev = to_pci_dev(hwif->dev);
-       struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
-       int s_time = t->setup, a_time = t->active, c_time = t->cycle;
-       u8 s_clc, a_clc, r_clc;
-       unsigned long flags;
+       ide_drive_t *pair = ide_get_pair_dev(drive);
        int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
-       int port = hwif->channel ? 0x5c : 0x58;
-       int portFIFO = hwif->channel ? 0x55 : 0x54;
-       u8 cd_dma_fifo = 0, unit = drive->dn & 1;
-
-       if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
-               s_clc = 0;
-       if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
-               a_clc = 0;
-
-       if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
-               r_clc = 1;
-       } else {
-               if (r_clc >= 16)
-                       r_clc = 0;
+       unsigned long T =  1000000 / bus_speed; /* PCI clock based */
+       struct ide_timing t;
+
+       ide_timing_compute(drive, drive->pio_mode, &t, T, 1);
+       if (pair) {
+               struct ide_timing p;
+
+               ide_timing_compute(pair, pair->pio_mode, &p, T, 1);
+               ide_timing_merge(&p, &t, &t,
+                       IDE_TIMING_SETUP | IDE_TIMING_8BIT);
+               if (pair->dma_mode) {
+                       ide_timing_compute(pair, pair->dma_mode, &p, T, 1);
+                       ide_timing_merge(&p, &t, &t,
+                               IDE_TIMING_SETUP | IDE_TIMING_8BIT);
+               }
        }
-       local_irq_save(flags);
-       
+
        /* 
         * PIO mode => ATA FIFO on, ATAPI FIFO off
         */
-       pci_read_config_byte(dev, portFIFO, &cd_dma_fifo);
-       if (drive->media==ide_disk) {
-               if (unit) {
-                       pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50);
-               } else {
-                       pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05);
-               }
-       } else {
-               if (unit) {
-                       pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F);
-               } else {
-                       pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0);
-               }
-       }
-       
-       pci_write_config_byte(dev, port, s_clc);
-       pci_write_config_byte(dev, port + unit + 2, (a_clc << 4) | r_clc);
-       local_irq_restore(flags);
+       ali_fifo_control(hwif, drive, (drive->media == ide_disk) ? 0x05 : 0x00);
+
+       ali_program_timings(hwif, drive, &t, 0);
 }
 
 /**
@@ -132,44 +155,42 @@ static u8 ali_udma_filter(ide_drive_t *drive)
 
 /**
  *     ali_set_dma_mode        -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Configure the hardware for the desired IDE transfer mode.
  */
 
-static void ali_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void ali_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
+       static u8 udma_timing[7] = { 0xC, 0xB, 0xA, 0x9, 0x8, 0xF, 0xD };
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
-       u8 speed1               = speed;
-       u8 unit                 = drive->dn & 1;
+       ide_drive_t *pair       = ide_get_pair_dev(drive);
+       int bus_speed           = ide_pci_clk ? ide_pci_clk : 33;
+       unsigned long T         =  1000000 / bus_speed; /* PCI clock based */
+       const u8 speed          = drive->dma_mode;
        u8 tmpbyte              = 0x00;
-       int m5229_udma          = (hwif->channel) ? 0x57 : 0x56;
-
-       if (speed == XFER_UDMA_6)
-               speed1 = 0x47;
+       struct ide_timing t;
 
        if (speed < XFER_UDMA_0) {
-               u8 ultra_enable = (unit) ? 0x7f : 0xf7;
-               /*
-                * clear "ultra enable" bit
-                */
-               pci_read_config_byte(dev, m5229_udma, &tmpbyte);
-               tmpbyte &= ultra_enable;
-               pci_write_config_byte(dev, m5229_udma, tmpbyte);
-
-               /*
-                * FIXME: Oh, my... DMA timings are never set.
-                */
+               ide_timing_compute(drive, drive->dma_mode, &t, T, 1);
+               if (pair) {
+                       struct ide_timing p;
+
+                       ide_timing_compute(pair, pair->pio_mode, &p, T, 1);
+                       ide_timing_merge(&p, &t, &t,
+                               IDE_TIMING_SETUP | IDE_TIMING_8BIT);
+                       if (pair->dma_mode) {
+                               ide_timing_compute(pair, pair->dma_mode,
+                                               &p, T, 1);
+                               ide_timing_merge(&p, &t, &t,
+                                       IDE_TIMING_SETUP | IDE_TIMING_8BIT);
+                       }
+               }
+               ali_program_timings(hwif, drive, &t, 0);
        } else {
-               pci_read_config_byte(dev, m5229_udma, &tmpbyte);
-               tmpbyte &= (0x0f << ((1-unit) << 2));
-               /*
-                * enable ultra dma and set timing
-                */
-               tmpbyte |= ((0x08 | ((4-speed1)&0x07)) << (unit << 2));
-               pci_write_config_byte(dev, m5229_udma, tmpbyte);
+               ali_program_timings(hwif, drive, NULL,
+                               udma_timing[speed - XFER_UDMA_0]);
                if (speed >= XFER_UDMA_3) {
                        pci_read_config_byte(dev, 0x4b, &tmpbyte);
                        tmpbyte |= 1;
@@ -355,19 +376,13 @@ static int ali_cable_override(struct pci_dev *pdev)
  *
  *     This checks if the controller and the cable are capable
  *     of UDMA66 transfers. It doesn't check the drives.
- *     But see note 2 below!
- *
- *     FIXME: frobs bits that are not defined on newer ALi devicea
  */
 
 static u8 ali_cable_detect(ide_hwif_t *hwif)
 {
        struct pci_dev *dev = to_pci_dev(hwif->dev);
-       unsigned long flags;
        u8 cbl = ATA_CBL_PATA40, tmpbyte;
 
-       local_irq_save(flags);
-
        if (m5229_revision >= 0xC2) {
                /*
                 * m5229 80-pin cable detection (from Host View)
@@ -387,8 +402,6 @@ static u8 ali_cable_detect(ide_hwif_t *hwif)
                }
        }
 
-       local_irq_restore(flags);
-
        return cbl;
 }
 
@@ -584,6 +597,6 @@ static void __exit ali15x3_ide_exit(void)
 module_init(ali15x3_ide_init);
 module_exit(ali15x3_ide_exit);
 
-MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox");
+MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("PCI driver module for ALi 15x3 IDE");
 MODULE_LICENSE("GPL");
index 628cd2e5fed83de28aab0b423f02adc15944e90d..3747b2561f099f7af257edb2b1fbebdbfb979f25 100644 (file)
@@ -3,7 +3,7 @@
  * IDE driver for Linux.
  *
  * Copyright (c) 2000-2002 Vojtech Pavlik
- * Copyright (c) 2007-2008 Bartlomiej Zolnierkiewicz
+ * Copyright (c) 2007-2010 Bartlomiej Zolnierkiewicz
  *
  * Based on the work of:
  *      Andre Hedrick
@@ -70,7 +70,8 @@ static void amd_set_speed(struct pci_dev *dev, u8 dn, u8 udma_mask,
        default: return;
        }
 
-       pci_write_config_byte(dev, AMD_UDMA_TIMING + offset + (3 - dn), t);
+       if (timing->udma)
+               pci_write_config_byte(dev, AMD_UDMA_TIMING + offset + 3 - dn, t);
 }
 
 /*
@@ -78,14 +79,14 @@ static void amd_set_speed(struct pci_dev *dev, u8 dn, u8 udma_mask,
  * to a desired transfer mode.  It also can be called by upper layers.
  */
 
-static void amd_set_drive(ide_drive_t *drive, const u8 speed)
+static void amd_set_drive(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct pci_dev *dev = to_pci_dev(hwif->dev);
        ide_drive_t *peer = ide_get_pair_dev(drive);
        struct ide_timing t, p;
        int T, UT;
        u8 udma_mask = hwif->ultra_mask;
+       const u8 speed = drive->dma_mode;
 
        T = 1000000000 / amd_clock;
        UT = (udma_mask == ATA_UDMA2) ? T : (T / 2);
@@ -93,7 +94,7 @@ static void amd_set_drive(ide_drive_t *drive, const u8 speed)
        ide_timing_compute(drive, speed, &t, T, UT);
 
        if (peer) {
-               ide_timing_compute(peer, peer->current_speed, &p, T, UT);
+               ide_timing_compute(peer, peer->pio_mode, &p, T, UT);
                ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
        }
 
@@ -107,9 +108,10 @@ static void amd_set_drive(ide_drive_t *drive, const u8 speed)
  * amd_set_pio_mode() is a callback from upper layers for PIO-only tuning.
  */
 
-static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void amd_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       amd_set_drive(drive, XFER_PIO_0 + pio);
+       drive->dma_mode = drive->pio_mode;
+       amd_set_drive(hwif, drive);
 }
 
 static void amd7409_cable_detect(struct pci_dev *dev)
@@ -340,6 +342,6 @@ static void __exit amd74xx_ide_exit(void)
 module_init(amd74xx_ide_init);
 module_exit(amd74xx_ide_exit);
 
-MODULE_AUTHOR("Vojtech Pavlik");
+MODULE_AUTHOR("Vojtech Pavlik, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("AMD PCI IDE driver");
 MODULE_LICENSE("GPL");
index 248219a89a68cdbf25bce00b93ec51075a47c393..000a78e5246c5c1d9f510fcfbbfc9cf35700137a 100644 (file)
@@ -172,11 +172,12 @@ static void at91_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
        leave_16bit(chipselect, mode);
 }
 
-static void at91_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void at91_ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        struct ide_timing *timing;
-       u8 chipselect = drive->hwif->select_data;
+       u8 chipselect = hwif->select_data;
        int use_iordy = 0;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        pdbg("chipselect %u pio %u\n", chipselect, pio);
 
index 837322b10a4c45875462f603ab1e59ee279b124e..15f0ead89f5cfac911e92a2a0df3279ec8088486 100644 (file)
@@ -42,19 +42,20 @@ static DEFINE_SPINLOCK(atiixp_lock);
 
 /**
  *     atiixp_set_pio_mode     -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Set the interface PIO mode.
  */
 
-static void atiixp_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void atiixp_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
+       struct pci_dev *dev = to_pci_dev(hwif->dev);
        unsigned long flags;
        int timing_shift = (drive->dn ^ 1) * 8;
        u32 pio_timing_data;
        u16 pio_mode_data;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        spin_lock_irqsave(&atiixp_lock, flags);
 
@@ -74,21 +75,22 @@ static void atiixp_set_pio_mode(ide_drive_t *drive, const u8 pio)
 
 /**
  *     atiixp_set_dma_mode     -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Set a ATIIXP host controller to the desired DMA mode.  This involves
  *     programming the right timing data into the PCI configuration space.
  */
 
-static void atiixp_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void atiixp_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
+       struct pci_dev *dev = to_pci_dev(hwif->dev);
        unsigned long flags;
        int timing_shift = (drive->dn ^ 1) * 8;
        u32 tmp32;
        u16 tmp16;
        u16 udma_ctl = 0;
+       const u8 speed = drive->dma_mode;
 
        spin_lock_irqsave(&atiixp_lock, flags);
 
index 349a67bf1a36c84eb2fb322d1c46211128ea3686..b26c23416fa775c715eaede8f5cca4b551440b4f 100644 (file)
@@ -99,12 +99,11 @@ static void au1xxx_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
 }
 #endif
 
-static void au1xxx_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void au1xxx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        int mem_sttime = 0, mem_stcfg = au_readl(MEM_STCFG2);
 
-       /* set pio mode! */
-       switch(pio) {
+       switch (drive->pio_mode - XFER_PIO_0) {
        case 0:
                mem_sttime = SBC_IDE_TIMING(PIO0);
 
@@ -161,11 +160,11 @@ static void au1xxx_set_pio_mode(ide_drive_t *drive, const u8 pio)
        au_writel(mem_stcfg,MEM_STCFG2);
 }
 
-static void auide_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void auide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        int mem_sttime = 0, mem_stcfg = au_readl(MEM_STCFG2);
 
-       switch(speed) {
+       switch (drive->dma_mode) {
 #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
        case XFER_MW_DMA_2:
                mem_sttime = SBC_IDE_TIMING(MDMA2);
@@ -297,8 +296,8 @@ static int auide_dma_test_irq(ide_drive_t *drive)
         */
        drive->waiting_for_dma++;
        if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) {
-               printk(KERN_WARNING "%s: timeout waiting for ddma to \
-                                     complete\n", drive->name);
+               printk(KERN_WARNING "%s: timeout waiting for ddma to complete\n",
+                      drive->name);
                return 1;
        }
        udelay(10);
index 1a32d62ed86b6bb611b8cd625a790587ef6b5735..d2b8b272bc27810a5dcfa4571e0eeb789ffd0e88 100644 (file)
@@ -572,9 +572,10 @@ static void cmd640_set_mode(ide_drive_t *drive, unsigned int index,
        program_drive_counts(drive, index);
 }
 
-static void cmd640_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cmd640_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        unsigned int index = 0, cycle_time;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        u8 b;
 
        switch (pio) {
@@ -605,7 +606,7 @@ static void cmd640_set_pio_mode(ide_drive_t *drive, const u8 pio)
 }
 #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
 
-static void cmd640_init_dev(ide_drive_t *drive)
+static void __init cmd640_init_dev(ide_drive_t *drive)
 {
        unsigned int i = drive->hwif->channel * 2 + (drive->dn & 1);
 
index f2500c8826bb0145b0315935dc3ba210512068dd..5f80312e636b25f05bd3732760d3536c24425242 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 1998          David S. Miller (davem@redhat.com)
  *
  * Copyright (C) 1999-2002     Andre Hedrick <andre@linux-ide.org>
+ * Copyright (C) 2007-2010     Bartlomiej Zolnierkiewicz
  * Copyright (C) 2007,2009     MontaVista Software, Inc. <source@mvista.com>
  */
 
 #define UDIDETCR1      0x7B
 #define DTPR1          0x7C
 
-static u8 quantize_timing(int timing, int quant)
-{
-       return (timing + quant - 1) / quant;
-}
-
-/*
- * This routine calculates active/recovery counts and then writes them into
- * the chipset registers.
- */
-static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_time)
+static void cmd64x_program_timings(ide_drive_t *drive, u8 mode)
 {
+       ide_hwif_t *hwif = drive->hwif;
        struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
-       int clock_time = 1000 / (ide_pci_clk ? ide_pci_clk : 33);
-       u8  cycle_count, active_count, recovery_count, drwtim;
+       int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
+       const unsigned long T = 1000000 / bus_speed;
        static const u8 recovery_values[] =
                {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
+       static const u8 setup_values[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
+       static const u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
        static const u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM2, DRWTIM3};
+       struct ide_timing t;
+       u8 arttim = 0;
 
-       cycle_count     = quantize_timing( cycle_time, clock_time);
-       active_count    = quantize_timing(active_time, clock_time);
-       recovery_count  = cycle_count - active_count;
+       ide_timing_compute(drive, mode, &t, T, 0);
 
        /*
         * In case we've got too long recovery phase, try to lengthen
         * the active phase
         */
-       if (recovery_count > 16) {
-               active_count += recovery_count - 16;
-               recovery_count = 16;
+       if (t.recover > 16) {
+               t.active += t.recover - 16;
+               t.recover = 16;
        }
-       if (active_count > 16)          /* shouldn't actually happen... */
-               active_count = 16;
+       if (t.active > 16)              /* shouldn't actually happen... */
+               t.active = 16;
 
        /*
         * Convert values to internal chipset representation
         */
-       recovery_count = recovery_values[recovery_count];
-       active_count  &= 0x0f;
+       t.recover = recovery_values[t.recover];
+       t.active &= 0x0f;
 
        /* Program the active/recovery counts into the DRWTIM register */
-       drwtim = (active_count << 4) | recovery_count;
-       (void) pci_write_config_byte(dev, drwtim_regs[drive->dn], drwtim);
-}
-
-/*
- * This routine writes into the chipset registers
- * PIO setup/active/recovery timings.
- */
-static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
-{
-       ide_hwif_t *hwif        = drive->hwif;
-       struct pci_dev *dev     = to_pci_dev(hwif->dev);
-       struct ide_timing *t    = ide_timing_find_mode(XFER_PIO_0 + pio);
-       unsigned long setup_count;
-       unsigned int cycle_time;
-       u8 arttim = 0;
-
-       static const u8 setup_values[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
-       static const u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
-
-       cycle_time = ide_pio_cycle_time(drive, pio);
-
-       program_cycle_times(drive, cycle_time, t->active);
-
-       setup_count = quantize_timing(t->setup,
-                       1000 / (ide_pci_clk ? ide_pci_clk : 33));
+       pci_write_config_byte(dev, drwtim_regs[drive->dn],
+                             (t.active << 4) | t.recover);
 
        /*
         * The primary channel has individual address setup timing registers
@@ -126,15 +97,21 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
        if (hwif->channel) {
                ide_drive_t *pair = ide_get_pair_dev(drive);
 
-               ide_set_drivedata(drive, (void *)setup_count);
+               if (pair) {
+                       struct ide_timing tp;
 
-               if (pair)
-                       setup_count = max_t(u8, setup_count,
-                                       (unsigned long)ide_get_drivedata(pair));
+                       ide_timing_compute(pair, pair->pio_mode, &tp, T, 0);
+                       ide_timing_merge(&t, &tp, &t, IDE_TIMING_SETUP);
+                       if (pair->dma_mode) {
+                               ide_timing_compute(pair, pair->dma_mode,
+                                               &tp, T, 0);
+                               ide_timing_merge(&tp, &t, &t, IDE_TIMING_SETUP);
+                       }
+               }
        }
 
-       if (setup_count > 5)            /* shouldn't actually happen... */
-               setup_count = 5;
+       if (t.setup > 5)                /* shouldn't actually happen... */
+               t.setup = 5;
 
        /*
         * Program the address setup clocks into the ARTTIM registers.
@@ -144,7 +121,7 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
        if (hwif->channel)
                arttim &= ~ARTTIM23_INTR_CH1;
        arttim &= ~0xc0;
-       arttim |= setup_values[setup_count];
+       arttim |= setup_values[t.setup];
        (void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim);
 }
 
@@ -153,8 +130,10 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
  * Special cases are 8: prefetch off, 9: prefetch on (both never worked)
  */
 
-static void cmd64x_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cmd64x_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
+
        /*
         * Filter out the prefetch control values
         * to prevent PIO5 from being programmed
@@ -162,20 +141,18 @@ static void cmd64x_set_pio_mode(ide_drive_t *drive, const u8 pio)
        if (pio == 8 || pio == 9)
                return;
 
-       cmd64x_tune_pio(drive, pio);
+       cmd64x_program_timings(drive, XFER_PIO_0 + pio);
 }
 
-static void cmd64x_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void cmd64x_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u8 unit                 = drive->dn & 0x01;
        u8 regU = 0, pciU       = hwif->channel ? UDIDETCR1 : UDIDETCR0;
+       const u8 speed          = drive->dma_mode;
 
-       if (speed >= XFER_SW_DMA_0) {
-               (void) pci_read_config_byte(dev, pciU, &regU);
-               regU &= ~(unit ? 0xCA : 0x35);
-       }
+       pci_read_config_byte(dev, pciU, &regU);
+       regU &= ~(unit ? 0xCA : 0x35);
 
        switch(speed) {
        case XFER_UDMA_5:
@@ -197,18 +174,13 @@ static void cmd64x_set_dma_mode(ide_drive_t *drive, const u8 speed)
                regU |= unit ? 0xC2 : 0x31;
                break;
        case XFER_MW_DMA_2:
-               program_cycle_times(drive, 120, 70);
-               break;
        case XFER_MW_DMA_1:
-               program_cycle_times(drive, 150, 80);
-               break;
        case XFER_MW_DMA_0:
-               program_cycle_times(drive, 480, 215);
+               cmd64x_program_timings(drive, speed);
                break;
        }
 
-       if (speed >= XFER_SW_DMA_0)
-               (void) pci_write_config_byte(dev, pciU, regU);
+       pci_write_config_byte(dev, pciU, regU);
 }
 
 static void cmd648_clear_irq(ide_drive_t *drive)
@@ -471,6 +443,6 @@ static void __exit cmd64x_ide_exit(void)
 module_init(cmd64x_ide_init);
 module_exit(cmd64x_ide_exit);
 
-MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick");
+MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("PCI driver module for CMD64x IDE");
 MODULE_LICENSE("GPL");
index 09f98ed0731fa251bfb08f46eea0e369eefe3f05..2c1e5f7cd261f79e82f947ae0cf7baaa3fc0505e 100644 (file)
@@ -57,11 +57,11 @@ static struct pio_clocks cs5520_pio_clocks[]={
        {1, 2, 1}
 };
 
-static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cs5520_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct pci_dev *pdev = to_pci_dev(hwif->dev);
        int controller = drive->dn > 1 ? 1 : 0;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        /* 8bit CAT/CRT - 8bit command timing for channel */
        pci_write_config_byte(pdev, 0x62 + controller, 
@@ -81,11 +81,12 @@ static void cs5520_set_pio_mode(ide_drive_t *drive, const u8 pio)
                (cs5520_pio_clocks[pio].assert));
 }
 
-static void cs5520_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void cs5520_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        printk(KERN_ERR "cs55x0: bad ide timing.\n");
 
-       cs5520_set_pio_mode(drive, 0);
+       drive->pio_mode = XFER_PIO_0 + 0;
+       cs5520_set_pio_mode(hwif, drive);
 }
 
 static const struct ide_port_ops cs5520_port_ops = {
index 40bf05eddf6ea1ef55c01e1c87be2ca9f88657b7..4dc4eb92b076f62218c14f7b966f2040fce6625b 100644 (file)
@@ -41,8 +41,8 @@ static unsigned int cs5530_pio_timings[2][5] = {
 
 /**
  *     cs5530_set_pio_mode     -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Handles setting of PIO mode for the chipset.
  *
@@ -50,10 +50,11 @@ static unsigned int cs5530_pio_timings[2][5] = {
  *     will have valid default PIO timings set up before we get here.
  */
 
-static void cs5530_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cs5530_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       unsigned long basereg = CS5530_BASEREG(drive->hwif);
+       unsigned long basereg = CS5530_BASEREG(hwif);
        unsigned int format = (inl(basereg + 4) >> 31) & 1;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        outl(cs5530_pio_timings[format][pio], basereg + ((drive->dn & 1)<<3));
 }
@@ -99,12 +100,12 @@ out:
        return mask;
 }
 
-static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode)
+static void cs5530_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        unsigned long basereg;
        unsigned int reg, timings = 0;
 
-       switch (mode) {
+       switch (drive->dma_mode) {
                case XFER_UDMA_0:       timings = 0x00921250; break;
                case XFER_UDMA_1:       timings = 0x00911140; break;
                case XFER_UDMA_2:       timings = 0x00911030; break;
@@ -112,7 +113,7 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode)
                case XFER_MW_DMA_1:     timings = 0x00012121; break;
                case XFER_MW_DMA_2:     timings = 0x00002020; break;
        }
-       basereg = CS5530_BASEREG(drive->hwif);
+       basereg = CS5530_BASEREG(hwif);
        reg = inl(basereg + 4);                 /* get drive0 config register */
        timings |= reg & 0x80000000;            /* preserve PIO format bit */
        if ((drive-> dn & 1) == 0) {            /* are we configuring drive0? */
index b883838adc241f068f5212972585cdb4cdb4d801..5059fafadf29d469568f2106766117d30069f294 100644 (file)
@@ -86,7 +86,7 @@ static void cs5535_set_speed(ide_drive_t *drive, const u8 speed)
                cmd = pioa = speed - XFER_PIO_0;
 
                if (pair) {
-                       u8 piob = ide_get_best_pio_mode(pair, 255, 4);
+                       u8 piob = pair->pio_mode - XFER_PIO_0;
 
                        if (piob < cmd)
                                cmd = piob;
@@ -129,28 +129,28 @@ static void cs5535_set_speed(ide_drive_t *drive, const u8 speed)
 
 /**
  *     cs5535_set_dma_mode     -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Programs the chipset for DMA mode.
  */
 
-static void cs5535_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void cs5535_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       cs5535_set_speed(drive, speed);
+       cs5535_set_speed(drive, drive->dma_mode);
 }
 
 /**
  *     cs5535_set_pio_mode     -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     A callback from the upper layers for PIO-only tuning.
  */
 
-static void cs5535_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cs5535_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       cs5535_set_speed(drive, XFER_PIO_0 + pio);
+       cs5535_set_speed(drive, drive->pio_mode);
 }
 
 static u8 cs5535_cable_detect(ide_hwif_t *hwif)
index 9623b852c616b4a3a27a6ef3277d03994960484a..24214ab60ac0b887a5262c8034d616459ac208d5 100644 (file)
@@ -125,11 +125,11 @@ static u8 cs5536_cable_detect(ide_hwif_t *hwif)
 
 /**
  *     cs5536_set_pio_mode             -       PIO timing setup
+ *     @hwif: ATA port
  *     @drive: ATA device
- *     @pio: PIO mode number
  */
 
-static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cs5536_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static const u8 drv_timings[5] = {
                0x98, 0x55, 0x32, 0x21, 0x20,
@@ -143,15 +143,16 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
                0x99, 0x92, 0x90, 0x22, 0x20,
        };
 
-       struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
+       struct pci_dev *pdev = to_pci_dev(hwif->dev);
        ide_drive_t *pair = ide_get_pair_dev(drive);
        int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
        unsigned long timings = (unsigned long)ide_get_drivedata(drive);
        u32 cast;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        u8 cmd_pio = pio;
 
        if (pair)
-               cmd_pio = min(pio, ide_get_best_pio_mode(pair, 255, 4));
+               cmd_pio = min_t(u8, pio, pair->pio_mode - XFER_PIO_0);
 
        timings &= (IDE_DRV_MASK << 8);
        timings |= drv_timings[pio];
@@ -172,11 +173,11 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
 
 /**
  *     cs5536_set_dma_mode             -       DMA timing setup
+ *     @hwif: ATA port
  *     @drive: ATA device
- *     @mode: DMA mode
  */
 
-static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
+static void cs5536_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static const u8 udma_timings[6] = {
                0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6,
@@ -186,10 +187,11 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
                0x67, 0x21, 0x20,
        };
 
-       struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
+       struct pci_dev *pdev = to_pci_dev(hwif->dev);
        int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT;
        unsigned long timings = (unsigned long)ide_get_drivedata(drive);
        u32 etc;
+       const u8 mode = drive->dma_mode;
 
        cs5536_read(pdev, ETC, &etc);
 
index d6e2cbbc53a09501953b39d83083038e8ed20c09..9383f67deae19c5924bda306e930a6cef38397f4 100644 (file)
@@ -1,43 +1,11 @@
 /*
  *  Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer
  *  Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>, Integrator
+ *  Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz
  *
  * CYPRESS CY82C693 chipset IDE controller
  *
  * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
- * Writing the driver was quite simple, since most of the job is
- * done by the generic pci-ide support.
- * The hard part was finding the CY82C693's datasheet on Cypress's
- * web page :-(. But Altavista solved this problem :-).
- *
- *
- * Notes:
- * - I recently got a 16.8G IBM DTTA, so I was able to test it with
- *   a large and fast disk - the results look great, so I'd say the
- *   driver is working fine :-)
- *   hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA
- * - this is my first linux driver, so there's probably a lot  of room
- *   for optimizations and bug fixing, so feel free to do it.
- * - if using PIO mode it's a good idea to set the PIO mode and
- *   32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
- * - I had some problems with my IBM DHEA with PIO modes < 2
- *   (lost interrupts) ?????
- * - first tests with DMA look okay, they seem to work, but there is a
- *   problem with sound - the BusMaster IDE TimeOut should fixed this
- *
- * Ancient History:
- * AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693
- * ASK@1999-01-23: v0.33 made a few minor code clean ups
- *                       removed DMA clock speed setting by default
- *                       added boot message
- * ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut
- *                       added support to set DMA Controller Clock Speed
- * ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes
- *                       on some drives.
- * ASK@1998-10-29: v0.3 added support to set DMA modes
- * ASK@1998-10-28: v0.2 added support to set PIO modes
- * ASK@1998-10-27: v0.1 first version - chipset detection
- *
  */
 
 #include <linux/module.h>
 #define CY82_INDEX_CHANNEL1    0x31
 #define CY82_INDEX_TIMEOUT     0x32
 
-/* the min and max PCI bus speed in MHz - from datasheet */
-#define CY82C963_MIN_BUS_SPEED 25
-#define CY82C963_MAX_BUS_SPEED 33
-
-/* the struct for the PIO mode timings */
-typedef struct pio_clocks_s {
-       u8      address_time;   /* Address setup (clocks) */
-       u8      time_16r;       /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
-       u8      time_16w;       /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
-       u8      time_8;         /* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
-} pio_clocks_t;
-
-/*
- * calc clocks using bus_speed
- * returns (rounded up) time in bus clocks for time in ns
- */
-static int calc_clk(int time, int bus_speed)
-{
-       int clocks;
-
-       clocks = (time*bus_speed+999)/1000 - 1;
-
-       if (clocks < 0)
-               clocks = 0;
-
-       if (clocks > 0x0F)
-               clocks = 0x0F;
-
-       return clocks;
-}
-
-/*
- * compute the values for the clock registers for PIO
- * mode and pci_clk [MHz] speed
- *
- * NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used
- *       for mode 3 and 4 drives 8 and 16-bit timings are the same
- *
- */
-static void compute_clocks(u8 pio, pio_clocks_t *p_pclk)
-{
-       struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
-       int clk1, clk2;
-       int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
-
-       /* we don't check against CY82C693's min and max speed,
-        * so you can play with the idebus=xx parameter
-        */
-
-       /* let's calc the address setup time clocks */
-       p_pclk->address_time = (u8)calc_clk(t->setup, bus_speed);
-
-       /* let's calc the active and recovery time clocks */
-       clk1 = calc_clk(t->active, bus_speed);
-
-       /* calc recovery timing */
-       clk2 = t->cycle - t->active - t->setup;
-
-       clk2 = calc_clk(clk2, bus_speed);
-
-       clk1 = (clk1<<4)|clk2;  /* combine active and recovery clocks */
-
-       /* note: we use the same values for 16bit IOR and IOW
-        *      those are all the same, since I don't have other
-        *      timings than those from ide-lib.c
-        */
-
-       p_pclk->time_16r = (u8)clk1;
-       p_pclk->time_16w = (u8)clk1;
-
-       /* what are good values for 8bit ?? */
-       p_pclk->time_8 = (u8)clk1;
-}
-
 /*
  * set DMA mode a specific channel for CY82C693
  */
 
-static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode)
+static void cy82c693_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
+       const u8 mode = drive->dma_mode;
        u8 single = (mode & 0x10) >> 4, index = 0, data = 0;
 
        index = hwif->channel ? CY82_INDEX_CHANNEL1 : CY82_INDEX_CHANNEL0;
@@ -186,12 +80,14 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode)
        outb(data, CY82_DATA_PORT);
 }
 
-static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void cy82c693_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct pci_dev *dev = to_pci_dev(hwif->dev);
-       pio_clocks_t pclk;
+       int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
+       const unsigned long T = 1000000 / bus_speed;
        unsigned int addrCtrl;
+       struct ide_timing t;
+       u8 time_16, time_8;
 
        /* select primary or secondary channel */
        if (hwif->index > 0) {  /* drive is on the secondary channel */
@@ -204,8 +100,12 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
                }
        }
 
-       /* let's calc the values for this PIO mode */
-       compute_clocks(pio, &pclk);
+       ide_timing_compute(drive, drive->pio_mode, &t, T, 1);
+
+       time_16 = clamp_val(t.recover - 1, 0, 15) |
+                 (clamp_val(t.active - 1, 0, 15) << 4);
+       time_8 = clamp_val(t.act8b - 1, 0, 15) |
+                (clamp_val(t.rec8b - 1, 0, 15) << 4);
 
        /* now let's write  the clocks registers */
        if ((drive->dn & 1) == 0) {
@@ -217,13 +117,13 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
                pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
 
                addrCtrl &= (~0xF);
-               addrCtrl |= (unsigned int)pclk.address_time;
+               addrCtrl |= clamp_val(t.setup - 1, 0, 15);
                pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
 
                /* now let's set the remaining registers */
-               pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
-               pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
-               pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
+               pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, time_16);
+               pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, time_16);
+               pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, time_8);
        } else {
                /*
                 * set slave drive
@@ -233,13 +133,13 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
                pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
 
                addrCtrl &= (~0xF0);
-               addrCtrl |= ((unsigned int)pclk.address_time<<4);
+               addrCtrl |= (clamp_val(t.setup - 1, 0, 15) << 4);
                pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
 
                /* now let's set the remaining registers */
-               pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
-               pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
-               pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
+               pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, time_16);
+               pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, time_16);
+               pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, time_8);
        }
 }
 
@@ -325,6 +225,6 @@ static void __exit cy82c693_ide_exit(void)
 module_init(cy82c693_ide_init);
 module_exit(cy82c693_ide_exit);
 
-MODULE_AUTHOR("Andreas Krebs, Andre Hedrick");
+MODULE_AUTHOR("Andreas Krebs, Andre Hedrick, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("PCI driver module for the Cypress CY82C693 IDE");
 MODULE_LICENSE("GPL");
index c6b138122981b7474b5f3f1381f4e798da9fb132..6929f7fce93a735114333709e8cd020f319c01d3 100644 (file)
@@ -68,11 +68,11 @@ static void sub22 (char b, char c)
 
 static DEFINE_SPINLOCK(dtc2278_lock);
 
-static void dtc2278_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void dtc2278_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        unsigned long flags;
 
-       if (pio >= 3) {
+       if (drive->pio_mode >= XFER_PIO_3) {
                spin_lock_irqsave(&dtc2278_lock, flags);
                /*
                 * This enables PIO mode4 (3?) on the first interface
index 4d90ac2dbb1be827aef1a633159ae835f701588d..b885c1d548f505f05048dadebd290e908c8aaeba 100644 (file)
@@ -627,14 +627,14 @@ static u32 get_speed_setting(u8 speed, struct hpt_info *info)
        return info->timings->clock_table[info->clock][i];
 }
 
-static void hpt3xx_set_mode(ide_drive_t *drive, const u8 speed)
+static void hpt3xx_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        struct hpt_info *info   = hpt3xx_get_info(hwif->dev);
        struct hpt_timings *t   = info->timings;
        u8  itr_addr            = 0x40 + (drive->dn * 4);
        u32 old_itr             = 0;
+       const u8 speed          = drive->dma_mode;
        u32 new_itr             = get_speed_setting(speed, info);
        u32 itr_mask            = speed < XFER_MW_DMA_0 ? t->pio_mask :
                                 (speed < XFER_UDMA_0   ? t->dma_mask :
@@ -651,9 +651,10 @@ static void hpt3xx_set_mode(ide_drive_t *drive, const u8 speed)
        pci_write_config_dword(dev, itr_addr, new_itr);
 }
 
-static void hpt3xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void hpt3xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       hpt3xx_set_mode(drive, XFER_PIO_0 + pio);
+       drive->dma_mode = drive->pio_mode;
+       hpt3xx_set_mode(hwif, drive);
 }
 
 static void hpt3xx_maskproc(ide_drive_t *drive, int mask)
index aafed8060e17a223e5b3dac63f6e0012195d1272..d81e49680c3fbc6a52fe89213ad01f9244c7cf34 100644 (file)
@@ -279,9 +279,10 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
 #endif
 }
 
-static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void ht6560b_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        unsigned long flags, config;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        u8 timing;
        
        switch (pio) {
index 0f67f1abbbd3b214793009d150cf2558b87704cc..4a697a238e280e2ce14c627d0d1f4bf053b86b45 100644 (file)
@@ -65,6 +65,8 @@ static struct cardinfo icside_cardinfo_v6_2 = {
 };
 
 struct icside_state {
+       unsigned int channel;
+       unsigned int enabled;
        void __iomem *irq_port;
        void __iomem *ioc_base;
        unsigned int sel;
@@ -114,11 +116,18 @@ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
        struct icside_state *state = ec->irq_data;
        void __iomem *base = state->irq_port;
 
-       writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
-       readb(base + ICS_ARCIN_V6_INTROFFSET_2);
+       state->enabled = 1;
 
-       writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
-       readb(base + ICS_ARCIN_V6_INTROFFSET_1);
+       switch (state->channel) {
+       case 0:
+               writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
+               readb(base + ICS_ARCIN_V6_INTROFFSET_2);
+               break;
+       case 1:
+               writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
+               readb(base + ICS_ARCIN_V6_INTROFFSET_1);
+               break;
+       }
 }
 
 /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
@@ -128,6 +137,8 @@ static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
 {
        struct icside_state *state = ec->irq_data;
 
+       state->enabled = 0;
+
        readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
        readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
 }
@@ -149,6 +160,44 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = {
        .irqpending     = icside_irqpending_arcin_v6,
 };
 
+/*
+ * Handle routing of interrupts.  This is called before
+ * we write the command to the drive.
+ */
+static void icside_maskproc(ide_drive_t *drive, int mask)
+{
+       ide_hwif_t *hwif = drive->hwif;
+       struct expansion_card *ec = ECARD_DEV(hwif->dev);
+       struct icside_state *state = ecard_get_drvdata(ec);
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       state->channel = hwif->channel;
+
+       if (state->enabled && !mask) {
+               switch (hwif->channel) {
+               case 0:
+                       writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
+                       readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
+                       break;
+               case 1:
+                       writeb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
+                       readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
+                       break;
+               }
+       } else {
+               readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
+               readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
+       }
+
+       local_irq_restore(flags);
+}
+
+static const struct ide_port_ops icside_v6_no_dma_port_ops = {
+       .maskproc               = icside_maskproc,
+};
+
 #ifdef CONFIG_BLK_DEV_IDEDMA_ICS
 /*
  * SG-DMA support.
@@ -185,10 +234,11 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = {
  *     MW1     80      50      50      150     C
  *     MW2     70      25      25      120     C
  */
-static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
+static void icside_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        unsigned long cycle_time;
        int use_dma_info = 0;
+       const u8 xfer_mode = drive->dma_mode;
 
        switch (xfer_mode) {
        case XFER_MW_DMA_2:
@@ -228,6 +278,7 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
 
 static const struct ide_port_ops icside_v6_port_ops = {
        .set_dma_mode           = icside_set_dma_mode,
+       .maskproc               = icside_maskproc,
 };
 
 static void icside_dma_host_set(ide_drive_t *drive, int on)
@@ -271,6 +322,11 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
         */
        BUG_ON(dma_channel_active(ec->dma));
 
+       /*
+        * Ensure that we have the right interrupt routed.
+        */
+       icside_maskproc(drive, 0);
+
        /*
         * Route the DMA signals to the correct interface.
         */
@@ -399,6 +455,7 @@ err_free:
 
 static const struct ide_port_info icside_v6_port_info __initdata = {
        .init_dma               = icside_dma_off_init,
+       .port_ops               = &icside_v6_no_dma_port_ops,
        .dma_ops                = &icside_v6_dma_ops,
        .host_flags             = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO,
        .mwdma_mask             = ATA_MWDMA2,
index dd6396384c25cd22f82149a4a188bd65aab35814..ab87e4f7cec913685fc8cdff98c727f260c18811 100644 (file)
@@ -121,19 +121,11 @@ static int ide_probe(struct pcmcia_device *link)
 static void ide_detach(struct pcmcia_device *link)
 {
     ide_info_t *info = link->priv;
-    ide_hwif_t *hwif = info->host->ports[0];
-    unsigned long data_addr, ctl_addr;
 
     dev_dbg(&link->dev, "ide_detach(0x%p)\n", link);
 
-    data_addr = hwif->io_ports.data_addr;
-    ctl_addr  = hwif->io_ports.ctl_addr;
-
     ide_release(link);
 
-    release_region(ctl_addr, 1);
-    release_region(data_addr, 8);
-
     kfree(info);
 } /* ide_detach */
 
@@ -354,12 +346,19 @@ static void ide_release(struct pcmcia_device *link)
 
     dev_dbg(&link->dev, "ide_release(0x%p)\n", link);
 
-    if (info->ndev)
-       /* FIXME: if this fails we need to queue the cleanup somehow
-          -- need to investigate the required PCMCIA magic */
+    if (info->ndev) {
+       ide_hwif_t *hwif = host->ports[0];
+       unsigned long data_addr, ctl_addr;
+
+       data_addr = hwif->io_ports.data_addr;
+       ctl_addr = hwif->io_ports.ctl_addr;
+
        ide_host_remove(host);
+       info->ndev = 0;
 
-    info->ndev = 0;
+       release_region(ctl_addr, 1);
+       release_region(data_addr, 8);
+    }
 
     pcmcia_disable_device(link);
 } /* ide_release */
index 1099bf7cf968ac216818e7ed0b6973e6cd6741ce..c6935c78757cfbfaacb06fb820223dc6fd19bec8 100644 (file)
@@ -105,15 +105,17 @@ static int set_pio_mode(ide_drive_t *drive, int arg)
                return -ENOSYS;
 
        if (set_pio_mode_abuse(drive->hwif, arg)) {
+               drive->pio_mode = arg + XFER_PIO_0;
+
                if (arg == 8 || arg == 9) {
                        unsigned long flags;
 
                        /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
                        spin_lock_irqsave(&hwif->lock, flags);
-                       port_ops->set_pio_mode(drive, arg);
+                       port_ops->set_pio_mode(hwif, drive);
                        spin_unlock_irqrestore(&hwif->lock, flags);
                } else
-                       port_ops->set_pio_mode(drive, arg);
+                       port_ops->set_pio_mode(hwif, drive);
        } else {
                int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
 
index 222c1ef65fb94f0dbc84e18d64b04feb9cba1285..376f2dc410c5b80df744d71e5efb921c6390d7bb 100644 (file)
@@ -231,7 +231,7 @@ u8 eighty_ninty_three(ide_drive_t *drive)
        u16 *id = drive->id;
        int ivb = ide_in_drive_list(id, ivb_list);
 
-       if (hwif->cbl == ATA_CBL_PATA40_SHORT)
+       if (hwif->cbl == ATA_CBL_SATA || hwif->cbl == ATA_CBL_PATA40_SHORT)
                return 1;
 
        if (ivb)
index f8c1ae6ad74c6b4ef46681e3942ce5c71b068fa5..fbedd35feb449194e85fe78ee110ccb5ba101e9f 100644 (file)
@@ -1042,6 +1042,8 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
                if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS)
                        drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
 
+               drive->pio_mode = XFER_PIO_0;
+
                if (port_ops && port_ops->init_dev)
                        port_ops->init_dev(drive);
        }
index 6a0e625421675a26eba76632ec8127d84eacfbeb..b07232880ec98869e43ed2b0ea4a969d423407dc 100644 (file)
@@ -1365,7 +1365,7 @@ static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count)
  * supported here, and not in the corresponding block interface. Our own
  * ide-tape ioctls are supported on both interfaces.
  */
-static int idetape_chrdev_ioctl(struct inode *inode, struct file *file,
+static long do_idetape_chrdev_ioctl(struct file *file,
                                unsigned int cmd, unsigned long arg)
 {
        struct ide_tape_obj *tape = file->private_data;
@@ -1420,6 +1420,16 @@ static int idetape_chrdev_ioctl(struct inode *inode, struct file *file,
        }
 }
 
+static long idetape_chrdev_ioctl(struct file *file,
+                               unsigned int cmd, unsigned long arg)
+{
+       long ret;
+       lock_kernel();
+       ret = do_idetape_chrdev_ioctl(file, cmd, arg);
+       unlock_kernel();
+       return ret;
+}
+
 /*
  * Do a mode sense page 0 with block descriptor and if it succeeds set the tape
  * block size with the reported value.
@@ -1888,7 +1898,7 @@ static const struct file_operations idetape_fops = {
        .owner          = THIS_MODULE,
        .read           = idetape_chrdev_read,
        .write          = idetape_chrdev_write,
-       .ioctl          = idetape_chrdev_ioctl,
+       .unlocked_ioctl = idetape_chrdev_ioctl,
        .open           = idetape_chrdev_open,
        .release        = idetape_chrdev_release,
 };
index 001a56365be577691ad8227c244d5ee88f01965d..0e05f75934c98c7fccb73286d6270a1011c215c6 100644 (file)
@@ -166,12 +166,13 @@ int ide_timing_compute(ide_drive_t *drive, u8 speed,
        if (id[ATA_ID_FIELD_VALID] & 2) {       /* EIDE drive */
                memset(&p, 0, sizeof(p));
 
-               if (speed <= XFER_PIO_2)
-                       p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO];
-               else if ((speed <= XFER_PIO_4) ||
-                        (speed == XFER_PIO_5 && !ata_id_is_cfa(id)))
-                       p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY];
-               else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
+               if (speed >= XFER_PIO_0 && speed < XFER_SW_DMA_0) {
+                       if (speed <= XFER_PIO_2)
+                               p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO];
+                       else if ((speed <= XFER_PIO_4) ||
+                                (speed == XFER_PIO_5 && !ata_id_is_cfa(id)))
+                               p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY];
+               } else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
                        p.cycle = id[ATA_ID_EIDE_DMA_MIN];
 
                ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B);
@@ -185,11 +186,10 @@ int ide_timing_compute(ide_drive_t *drive, u8 speed,
        /*
         * Even in DMA/UDMA modes we still use PIO access for IDENTIFY,
         * S.M.A.R.T and some other commands. We have to ensure that the
-        * DMA cycle timing is slower/equal than the fastest PIO timing.
+        * DMA cycle timing is slower/equal than the current PIO timing.
         */
        if (speed >= XFER_SW_DMA_0) {
-               u8 pio = ide_get_best_pio_mode(drive, 255, 5);
-               ide_timing_compute(drive, XFER_PIO_0 + pio, &p, T, UT);
+               ide_timing_compute(drive, drive->pio_mode, &p, T, UT);
                ide_timing_merge(&p, t, t, IDE_TIMING_ALL);
        }
 
index 46d203ce60cc4ab879be08a85b2f2a8540ddd88e..5fc8d5c17de9a243c20b68c2239128e56b9a57f6 100644 (file)
@@ -58,7 +58,7 @@ EXPORT_SYMBOL(ide_xfer_verbose);
  *     This is used by most chipset support modules when "auto-tuning".
  */
 
-u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
+static u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
 {
        u16 *id = drive->id;
        int pio_mode = -1, overridden = 0;
@@ -105,7 +105,6 @@ u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
 
        return pio_mode;
 }
-EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
 
 int ide_pio_need_iordy(ide_drive_t *drive, const u8 pio)
 {
@@ -135,17 +134,20 @@ int ide_set_pio_mode(ide_drive_t *drive, const u8 mode)
         * set transfer mode on the device in ->set_pio_mode method...
         */
        if (port_ops->set_dma_mode == NULL) {
-               port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
+               drive->pio_mode = mode;
+               port_ops->set_pio_mode(hwif, drive);
                return 0;
        }
 
        if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
                if (ide_config_drive_speed(drive, mode))
                        return -1;
-               port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
+               drive->pio_mode = mode;
+               port_ops->set_pio_mode(hwif, drive);
                return 0;
        } else {
-               port_ops->set_pio_mode(drive, mode - XFER_PIO_0);
+               drive->pio_mode = mode;
+               port_ops->set_pio_mode(hwif, drive);
                return ide_config_drive_speed(drive, mode);
        }
 }
@@ -164,10 +166,12 @@ int ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
        if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) {
                if (ide_config_drive_speed(drive, mode))
                        return -1;
-               port_ops->set_dma_mode(drive, mode);
+               drive->dma_mode = mode;
+               port_ops->set_dma_mode(hwif, drive);
                return 0;
        } else {
-               port_ops->set_dma_mode(drive, mode);
+               drive->dma_mode = mode;
+               port_ops->set_dma_mode(hwif, drive);
                return ide_config_drive_speed(drive, mode);
        }
 }
index 0d266a5b524d6ad822d0cfaa7c858539dc59a502..560e66d07659b46f8ccbc2fef17dccf26f963f96 100644 (file)
 
 #define DRV_NAME "IT8172"
 
-static void it8172_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void it8172_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u16 drive_enables;
        u32 drive_timing;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        /*
         * The highest value of DIOR/DIOW pulse width and recovery time
@@ -77,14 +77,14 @@ static void it8172_set_pio_mode(ide_drive_t *drive, const u8 pio)
        pci_write_config_dword(dev, 0x44, drive_timing);
 }
 
-static void it8172_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void it8172_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        int a_speed             = 3 << (drive->dn * 4);
        int u_flag              = 1 << drive->dn;
        int u_speed             = 0;
        u8 reg48, reg4a;
+       const u8 speed          = drive->dma_mode;
 
        pci_read_config_byte(dev, 0x48, &reg48);
        pci_read_config_byte(dev, 0x4a, &reg4a);
@@ -98,14 +98,14 @@ static void it8172_set_dma_mode(ide_drive_t *drive, const u8 speed)
                pci_write_config_byte(dev, 0x4a, reg4a | u_speed);
        } else {
                const u8 mwdma_to_pio[] = { 0, 3, 4 };
-               u8 pio;
 
                pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
                pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed);
 
-               pio = mwdma_to_pio[speed - XFER_MW_DMA_0];
+               drive->pio_mode =
+                       mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
 
-               it8172_set_pio_mode(drive, pio);
+               it8172_set_pio_mode(hwif, drive);
        }
 }
 
index 47976167796a4a03fd35a44dbf1c2b372a920739..46816ba26416d30ebe650ead5e99bd96532f558f 100644 (file)
 
 /**
  *     it8213_set_pio_mode     -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Set the interface PIO mode.
  */
 
-static void it8213_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void it8213_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        int is_slave            = drive->dn & 1;
        int master_port         = 0x40;
@@ -35,6 +34,7 @@ static void it8213_set_pio_mode(ide_drive_t *drive, const u8 pio)
        u8 slave_data;
        static DEFINE_SPINLOCK(tune_lock);
        int control = 0;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        static const u8 timings[][2] = {
                                        { 0, 0 },
@@ -74,15 +74,14 @@ static void it8213_set_pio_mode(ide_drive_t *drive, const u8 pio)
 
 /**
  *     it8213_set_dma_mode     -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Tune the ITE chipset for the DMA mode.
  */
 
-static void it8213_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void it8213_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u8 maslave              = 0x40;
        int a_speed             = 3 << (drive->dn * 4);
@@ -92,6 +91,7 @@ static void it8213_set_dma_mode(ide_drive_t *drive, const u8 speed)
        int u_speed             = 0;
        u16                     reg4042, reg4a;
        u8                      reg48, reg54, reg55;
+       const u8 speed          = drive->dma_mode;
 
        pci_read_config_word(dev, maslave, &reg4042);
        pci_read_config_byte(dev, 0x48, &reg48);
@@ -120,7 +120,6 @@ static void it8213_set_dma_mode(ide_drive_t *drive, const u8 speed)
                        pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
        } else {
                const u8 mwdma_to_pio[] = { 0, 3, 4 };
-               u8 pio;
 
                if (reg48 & u_flag)
                        pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
@@ -132,11 +131,12 @@ static void it8213_set_dma_mode(ide_drive_t *drive, const u8 speed)
                        pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
 
                if (speed >= XFER_MW_DMA_0)
-                       pio = mwdma_to_pio[speed - XFER_MW_DMA_0];
+                       drive->pio_mode =
+                               mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
                else
-                       pio = 2; /* only SWDMA2 is allowed */
+                       drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */
 
-               it8213_set_pio_mode(drive, pio);
+               it8213_set_pio_mode(hwif, drive);
        }
 }
 
index 51aa745246dcb9551af49161014f533bd4d4e43c..b2709c7334852b69badfd0f0314b02b1f020d8b5 100644 (file)
@@ -228,18 +228,18 @@ static void it821x_clock_strategy(ide_drive_t *drive)
 
 /**
  *     it821x_set_pio_mode     -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Tune the host to the desired PIO mode taking into the consideration
  *     the maximum PIO mode supported by the other device on the cable.
  */
 
-static void it821x_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void it821x_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct it821x_dev *itdev = ide_get_hwifdata(hwif);
        ide_drive_t *pair = ide_get_pair_dev(drive);
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        u8 unit = drive->dn & 1, set_pio = pio;
 
        /* Spec says 89 ref driver uses 88 */
@@ -252,7 +252,7 @@ static void it821x_set_pio_mode(ide_drive_t *drive, const u8 pio)
         * on the cable.
         */
        if (pair) {
-               u8 pair_pio = ide_get_best_pio_mode(pair, 255, 4);
+               u8 pair_pio = pair->pio_mode - XFER_PIO_0;
                /* trim PIO to the slowest of the master/slave */
                if (pair_pio < set_pio)
                        set_pio = pair_pio;
@@ -393,14 +393,16 @@ static int it821x_dma_end(ide_drive_t *drive)
 
 /**
  *     it821x_set_dma_mode     -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Tune the ITE chipset for the desired DMA mode.
  */
 
-static void it821x_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void it821x_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
+       const u8 speed = drive->dma_mode;
+
        /*
         * MWDMA tuning is really hard because our MWDMA and PIO
         * timings are kept in the same place.  We can switch in the
index bf2be6431b204d0a593b898a58ce4286a0e3f48b..74c2c4a6d909703b6c9cf7a605967f60cfda0149 100644 (file)
@@ -80,19 +80,19 @@ static u8 jmicron_cable_detect(ide_hwif_t *hwif)
        return ATA_CBL_PATA80;
 }
 
-static void jmicron_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void jmicron_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
 }
 
 /**
  *     jmicron_set_dma_mode    -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @mode: DMA mode
  *
  *     As the JMicron snoops for timings we don't need to do anything here.
  */
 
-static void jmicron_set_dma_mode(ide_drive_t *drive, const u8 mode)
+static void jmicron_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
 }
 
index f1d70d6630fe7191baa3661b5f33c8afc8287323..1a53a4c375ed4fdf998ac4be336dc1d79e2b82a3 100644 (file)
@@ -8,77 +8,6 @@
  * Jan Harkes <jaharkes@cwi.nl>,
  * Mark Lord <mlord@pobox.com>
  * Some parts of code are from ali14xx.c and from rz1000.c.
- *
- * OPTi is trademark of OPTi, Octek is trademark of Octek.
- *
- * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
- * and disassembled/traced setupvic.exe (DOS program).
- * It increases kernel code about 2 kB.
- * I don't have this card no more, but I hope I can get some in case
- * of needed development.
- * My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
- * It has a place for a secondary connector in circuit, but nothing
- * is there. Also BIOS says no address for
- * secondary controller (see bellow in ide_init_opti621).
- * I've only tested this on my system, which only has one disk.
- * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
- * is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random
- * lockups). I tried the OCTEK double speed CD-ROM and
- * it does not work! But I can't boot DOS also, so it's probably
- * hardware fault. I have connected Conner 80MB, the Seagate 850MB (no
- * problems) and Seagate 1GB (as slave, WD as master). My experiences
- * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
- * it slows to about 100kB/s! I don't know why and I have
- * not this drive now, so I can't try it again.
- * I write this driver because I lost the paper ("manual") with
- * settings of jumpers on the card and I have to boot Linux with
- * Loadlin except LILO, cause I have to run the setupvic.exe program
- * already or I get disk errors (my test: rpm -Vf
- * /usr/X11R6/bin/XF86_SVGA - or any big file).
- * Some numbers from hdparm -t /dev/hda:
- * Timing buffer-cache reads:   32 MB in  3.02 seconds =10.60 MB/sec
- * Timing buffered disk reads:  16 MB in  5.52 seconds = 2.90 MB/sec
- * I have 4 Megs/s before, but I don't know why (maybe changes
- * in hdparm test).
- * After release of 0.1, I got some successful reports, so it might work.
- *
- * The main problem with OPTi is that some timings for master
- * and slave must be the same. For example, if you have master
- * PIO 3 and slave PIO 0, driver have to set some timings of
- * master for PIO 0. Second problem is that opti621_set_pio_mode
- * got only one drive to set, but have to set both drives.
- * This is solved in compute_pios. If you don't set
- * the second drive, compute_pios use ide_get_best_pio_mode
- * for autoselect mode (you can change it to PIO 0, if you want).
- * If you then set the second drive to another PIO, the old value
- * (automatically selected) will be overrided by yours.
- * There is a 25/33MHz switch in configuration
- * register, but driver is written for use at any frequency.
- *
- * Version 0.1, Nov 8, 1996
- * by Jaromir Koutek, for 2.1.8.
- * Initial version of driver.
- *
- * Version 0.2
- * Number 0.2 skipped.
- *
- * Version 0.3, Nov 29, 1997
- * by Mark Lord (probably), for 2.1.68
- * Updates for use with new IDE block driver.
- *
- * Version 0.4, Dec 14, 1997
- * by Jan Harkes
- * Fixed some errors and cleaned the code.
- *
- * Version 0.5, Jan 2, 1998
- * by Jaromir Koutek
- * Updates for use with (again) new IDE block driver.
- * Update of documentation.
- *
- * Version 0.6, Jan 2, 1999
- * by Jaromir Koutek
- * Reversed to version 0.3 of the driver, because
- * 0.5 doesn't work.
  */
 
 #include <linux/types.h>
@@ -133,12 +62,12 @@ static u8 read_reg(int reg)
        return ret;
 }
 
-static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void opti621_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        ide_drive_t *pair = ide_get_pair_dev(drive);
        unsigned long flags;
-       unsigned long mode = XFER_PIO_0 + pio, pair_mode;
+       unsigned long mode = drive->pio_mode, pair_mode;
+       const u8 pio = mode - XFER_PIO_0;
        u8 tim, misc, addr_pio = pio, clk;
 
        /* DRDY is default 2 (by OPTi Databook) */
index f8eddf05ecb8533ebca9e9134c56c097a65170bd..9e8f4e1b0cc9c4c650a375e47c35712cd6696970 100644 (file)
@@ -166,7 +166,7 @@ static void palm_bk3710_setpiomode(void __iomem *base, ide_drive_t *mate,
        writel(val32, base + BK3710_DATRCVR);
 
        if (mate) {
-               u8 mode2 = ide_get_best_pio_mode(mate, 255, 4);
+               u8 mode2 = mate->pio_mode - XFER_PIO_0;
 
                if (mode2 < mode)
                        mode = mode2;
@@ -188,10 +188,11 @@ static void palm_bk3710_setpiomode(void __iomem *base, ide_drive_t *mate,
        writel(val32, base + BK3710_REGRCVR);
 }
 
-static void palm_bk3710_set_dma_mode(ide_drive_t *drive, u8 xferspeed)
+static void palm_bk3710_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        int is_slave = drive->dn & 1;
-       void __iomem *base = (void *)drive->hwif->dma_base;
+       void __iomem *base = (void *)hwif->dma_base;
+       const u8 xferspeed = drive->dma_mode;
 
        if (xferspeed >= XFER_UDMA_0) {
                palm_bk3710_setudmamode(base, is_slave,
@@ -203,12 +204,13 @@ static void palm_bk3710_set_dma_mode(ide_drive_t *drive, u8 xferspeed)
        }
 }
 
-static void palm_bk3710_set_pio_mode(ide_drive_t *drive, u8 pio)
+static void palm_bk3710_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        unsigned int cycle_time;
        int is_slave = drive->dn & 1;
        ide_drive_t *mate;
-       void __iomem *base = (void *)drive->hwif->dma_base;
+       void __iomem *base = (void *)hwif->dma_base;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        /*
         * Obtain the drive PIO data for tuning the Palm Chip registers
index 65ba8239e7b563496ca93c905841f5c731d9e2e3..9546fe2a93f7f5560175d590af1203e25b8821ea 100644 (file)
@@ -129,11 +129,11 @@ static struct udma_timing {
        { 0x1a, 0x01, 0xcb },   /* UDMA mode 6 */
 };
 
-static void pdcnew_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void pdcnew_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u8 adj                  = (drive->dn & 1) ? 0x08 : 0x00;
+       const u8 speed          = drive->dma_mode;
 
        /*
         * IDE core issues SETFEATURES_XFER to the drive first (thanks to
@@ -167,11 +167,11 @@ static void pdcnew_set_dma_mode(ide_drive_t *drive, const u8 speed)
        }
 }
 
-static void pdcnew_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void pdcnew_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct pci_dev *dev = to_pci_dev(hwif->dev);
        u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        if (max_dma_rate(dev) == 4) {
                set_indexed_reg(hwif, 0x0c + adj, pio_timings[pio].reg0c);
index 35161dd840a0550559d3f66f2ec1851182a97ab8..c5f3841af360da3d8d62a4fc10bb10b7160f21e0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1998-2002            Andre Hedrick <andre@linux-ide.org>
  *  Copyright (C) 2006-2007, 2009      MontaVista Software, Inc.
- *  Copyright (C) 2007                 Bartlomiej Zolnierkiewicz
+ *  Copyright (C) 2007-2010            Bartlomiej Zolnierkiewicz
  *
  *  Portions Copyright (C) 1999 Promise Technology, Inc.
  *  Author: Frank Tiernan (frankt@promise.com)
 
 #define DRV_NAME "pdc202xx_old"
 
-static void pdc_old_disable_66MHz_clock(ide_hwif_t *);
-
-static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
+static void pdc202xx_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u8 drive_pci            = 0x60 + (drive->dn << 2);
+       const u8 speed          = drive->dma_mode;
 
        u8                      AP = 0, BP = 0, CP = 0;
        u8                      TA = 0, TB = 0, TC = 0;
 
-       /*
-        * TODO: do this once per channel
-        */
-       if (dev->device != PCI_DEVICE_ID_PROMISE_20246)
-               pdc_old_disable_66MHz_clock(hwif);
-
        pci_read_config_byte(dev, drive_pci,     &AP);
        pci_read_config_byte(dev, drive_pci + 1, &BP);
        pci_read_config_byte(dev, drive_pci + 2, &CP);
@@ -84,9 +76,10 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
        }
 }
 
-static void pdc202xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void pdc202xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       pdc202xx_set_mode(drive, XFER_PIO_0 + pio);
+       drive->dma_mode = drive->pio_mode;
+       pdc202xx_set_mode(hwif, drive);
 }
 
 static int pdc202xx_test_irq(ide_hwif_t *hwif)
@@ -100,13 +93,13 @@ static int pdc202xx_test_irq(ide_hwif_t *hwif)
                 * bit 7: error, bit 6: interrupting,
                 * bit 5: FIFO full, bit 4: FIFO empty
                 */
-               return ((sc1d & 0x50) == 0x40) ? 1 : 0;
+               return ((sc1d & 0x50) == 0x50) ? 1 : 0;
        } else  {
                /*
                 * bit 3: error, bit 2: interrupting,
                 * bit 1: FIFO full, bit 0: FIFO empty
                 */
-               return ((sc1d & 0x05) == 0x04) ? 1 : 0;
+               return ((sc1d & 0x05) == 0x05) ? 1 : 0;
        }
 }
 
@@ -145,6 +138,11 @@ static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif)
        outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg);
 }
 
+static void pdc2026x_init_hwif(ide_hwif_t *hwif)
+{
+       pdc_old_disable_66MHz_clock(hwif);
+}
+
 static void pdc202xx_dma_start(ide_drive_t *drive)
 {
        if (drive->current_speed > XFER_UDMA_2)
@@ -261,6 +259,7 @@ static const struct ide_dma_ops pdc2026x_dma_ops = {
        { \
                .name           = DRV_NAME, \
                .init_chipset   = init_chipset_pdc202xx, \
+               .init_hwif      = pdc2026x_init_hwif, \
                .port_ops       = &pdc2026x_port_ops, \
                .dma_ops        = &pdc2026x_dma_ops, \
                .host_flags     = IDE_HFLAGS_PDC202XX, \
@@ -356,6 +355,6 @@ static void __exit pdc202xx_ide_exit(void)
 module_init(pdc202xx_ide_init);
 module_exit(pdc202xx_ide_exit);
 
-MODULE_AUTHOR("Andre Hedrick, Frank Tiernan");
+MODULE_AUTHOR("Andre Hedrick, Frank Tiernan, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("PCI driver module for older Promise IDE");
 MODULE_LICENSE("GPL");
index bf14f39bd3a78c470a7333ee7ebe07bb191e4cc8..1bdca49e5a0352f8c9f0fdadd281de7e653b96dd 100644 (file)
@@ -59,15 +59,14 @@ static int no_piix_dma;
 
 /**
  *     piix_set_pio_mode       -       set host controller for PIO mode
+ *     @port: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Set the interface PIO mode based upon the settings done by AMI BIOS.
  */
 
-static void piix_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void piix_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        int is_slave            = drive->dn & 1;
        int master_port         = hwif->channel ? 0x42 : 0x40;
@@ -77,6 +76,7 @@ static void piix_set_pio_mode(ide_drive_t *drive, const u8 pio)
        u8 slave_data;
        static DEFINE_SPINLOCK(tune_lock);
        int control = 0;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
                                     /* ISP  RTC */
        static const u8 timings[][2]= {
@@ -127,16 +127,15 @@ static void piix_set_pio_mode(ide_drive_t *drive, const u8 pio)
 
 /**
  *     piix_set_dma_mode       -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Set a PIIX host controller to the desired DMA mode.  This involves
  *     programming the right timing data into the PCI configuration space.
  */
 
-static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void piix_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u8 maslave              = hwif->channel ? 0x42 : 0x40;
        int a_speed             = 3 << (drive->dn * 4);
@@ -147,6 +146,7 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed)
        int                     sitre;
        u16                     reg4042, reg4a;
        u8                      reg48, reg54, reg55;
+       const u8 speed          = drive->dma_mode;
 
        pci_read_config_word(dev, maslave, &reg4042);
        sitre = (reg4042 & 0x4000) ? 1 : 0;
@@ -176,7 +176,6 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed)
                        pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
        } else {
                const u8 mwdma_to_pio[] = { 0, 3, 4 };
-               u8 pio;
 
                if (reg48 & u_flag)
                        pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
@@ -188,11 +187,12 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed)
                        pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
 
                if (speed >= XFER_MW_DMA_0)
-                       pio = mwdma_to_pio[speed - XFER_MW_DMA_0];
+                       drive->pio_mode =
+                               mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
                else
-                       pio = 2; /* only SWDMA2 is allowed */
+                       drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */
 
-               piix_set_pio_mode(drive, pio);
+               piix_set_pio_mode(hwif, drive);
        }
 }
 
index 7a4e788cab2f54d5c031cb814e2d42db7ca0e855..850ee452e9bb34db31d833cab4a9f66674b92658 100644 (file)
@@ -496,12 +496,11 @@ static void pmac_write_devctl(ide_hwif_t *hwif, u8 ctl)
 /*
  * Old tuning functions (called on hdparm -p), sets up drive PIO timings
  */
-static void
-pmac_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void pmac_ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        pmac_ide_hwif_t *pmif =
                (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        struct ide_timing *tim = ide_timing_find_mode(XFER_PIO_0 + pio);
        u32 *timings, t;
        unsigned accessTicks, recTicks;
@@ -778,14 +777,14 @@ set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
 #endif 
 }
 
-static void pmac_ide_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void pmac_ide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        pmac_ide_hwif_t *pmif =
                (pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
        int ret = 0;
        u32 *timings, *timings2, tl[2];
        u8 unit = drive->dn & 1;
+       const u8 speed = drive->dma_mode;
 
        timings = &pmif->timings[unit];
        timings2 = &pmif->timings[unit+2];
@@ -1651,8 +1650,8 @@ pmac_ide_dma_test_irq (ide_drive_t *drive)
                if ((status & FLUSH) == 0)
                        break;
                if (++timeout > 100) {
-                       printk(KERN_WARNING "ide%d, ide_dma_test_irq \
-                       timeout flushing channel\n", hwif->index);
+                       printk(KERN_WARNING "ide%d, ide_dma_test_irq timeout flushing channel\n",
+                              hwif->index);
                        break;
                }
        }       
index 74696edc8d1dec7e94cb04621a04a782d75aeee4..3f0244fd8e623010433c5086c1656bf812a62fbb 100644 (file)
@@ -189,15 +189,13 @@ static void qd_set_timing (ide_drive_t *drive, u8 timing)
        printk(KERN_DEBUG "%s: %#x\n", drive->name, timing);
 }
 
-static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void qd6500_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        u16 *id = drive->id;
        int active_time   = 175;
        int recovery_time = 415; /* worst case values from the dos driver */
 
-       /*
-        * FIXME: use "pio" value
-        */
+       /* FIXME: use drive->pio_mode value */
        if (!qd_find_disk_type(drive, &active_time, &recovery_time) &&
            (id[ATA_ID_OLD_PIO_MODES] & 0xff) && (id[ATA_ID_FIELD_VALID] & 2) &&
            id[ATA_ID_EIDE_PIO] >= 240) {
@@ -211,9 +209,9 @@ static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio)
                                active_time, recovery_time));
 }
 
-static void qd6580_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void qd6580_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
        unsigned int cycle_time;
        int active_time   = 175;
index d467478d68da96839efd99b665b552e2f7512bbc..134f1fd138665e01d3d7a3bb84ae431104f87db4 100644 (file)
@@ -122,13 +122,13 @@ out:
        return mask;
 }
 
-static void sc1200_set_dma_mode(ide_drive_t *drive, const u8 mode)
+static void sc1200_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t              *hwif = drive->hwif;
        struct pci_dev          *dev = to_pci_dev(hwif->dev);
        unsigned int            reg, timings;
        unsigned short          pci_clock;
        unsigned int            basereg = hwif->channel ? 0x50 : 0x40;
+       const u8                mode = drive->dma_mode;
 
        static const u32 udma_timing[3][3] = {
                { 0x00921250, 0x00911140, 0x00911030 },
@@ -193,10 +193,10 @@ static int sc1200_dma_end(ide_drive_t *drive)
  * will have valid default PIO timings set up before we get here.
  */
 
-static void sc1200_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void sc1200_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t      *hwif = drive->hwif;
        int             mode = -1;
+       const u8        pio = drive->pio_mode - XFER_PIO_0;
 
        /*
         * bad abuse of ->set_pio_mode interface
index 1104bb301eb98eb4f2d22b78a05ba33d1d99761c..b7f5b0c4310c2e18558f222b217b1ed9a4f3e0b4 100644 (file)
@@ -199,16 +199,15 @@ scc_ide_outsl(unsigned long port, void *addr, u32 count)
 
 /**
  *     scc_set_pio_mode        -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Load the timing settings for this device mode into the
  *     controller.
  */
 
-static void scc_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void scc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct scc_ports *ports = ide_get_hwifdata(hwif);
        unsigned long ctl_base = ports->ctl;
        unsigned long cckctrl_port = ctl_base + 0xff0;
@@ -216,6 +215,7 @@ static void scc_set_pio_mode(ide_drive_t *drive, const u8 pio)
        unsigned long pioct_port = ctl_base + 0x004;
        unsigned long reg;
        int offset;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        reg = in_be32((void __iomem *)cckctrl_port);
        if (reg & CCKCTRL_ATACLKOEN) {
@@ -231,16 +231,15 @@ static void scc_set_pio_mode(ide_drive_t *drive, const u8 pio)
 
 /**
  *     scc_set_dma_mode        -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Load the timing settings for this device mode into the
  *     controller.
  */
 
-static void scc_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void scc_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct scc_ports *ports = ide_get_hwifdata(hwif);
        unsigned long ctl_base = ports->ctl;
        unsigned long cckctrl_port = ctl_base + 0xff0;
@@ -254,6 +253,7 @@ static void scc_set_dma_mode(ide_drive_t *drive, const u8 speed)
        int offset, idx;
        unsigned long reg;
        unsigned long jcactsel;
+       const u8 speed = drive->dma_mode;
 
        reg = in_be32((void __iomem *)cckctrl_port);
        if (reg & CCKCTRL_ATACLKOEN) {
@@ -872,20 +872,18 @@ static struct pci_driver scc_pci_driver = {
        .remove = __devexit_p(scc_remove),
 };
 
-static int scc_ide_init(void)
+static int __init scc_ide_init(void)
 {
        return ide_pci_register_driver(&scc_pci_driver);
 }
 
-module_init(scc_ide_init);
-/* -- No exit code?
-static void scc_ide_exit(void)
+static void __exit scc_ide_exit(void)
 {
-       ide_pci_unregister_driver(&scc_pci_driver);
+       pci_unregister_driver(&scc_pci_driver);
 }
-module_exit(scc_ide_exit);
- */
 
+module_init(scc_ide_init);
+module_exit(scc_ide_exit);
 
 MODULE_DESCRIPTION("PCI driver module for Toshiba SCC IDE");
 MODULE_LICENSE("GPL");
index b6554ef92716003567333482982458dd32a0db9b..35fb8dabb55dc4e4cf5e32217095ea819b1c4f9f 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1998-2000 Michel Aubry
  * Copyright (C) 1998-2000 Andrzej Krzysztofowicz
  * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
- * Copyright (C)      2007 Bartlomiej Zolnierkiewicz
+ * Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz
  * Portions copyright (c) 2001 Sun Microsystems
  *
  *
@@ -52,8 +52,6 @@ static const char *svwks_bad_ata100[] = {
        NULL
 };
 
-static struct pci_dev *isa_dev;
-
 static int check_in_drive_lists (ide_drive_t *drive, const char **list)
 {
        char *m = (char *)&drive->id[ATA_ID_PROD];
@@ -67,26 +65,14 @@ static int check_in_drive_lists (ide_drive_t *drive, const char **list)
 static u8 svwks_udma_filter(ide_drive_t *drive)
 {
        struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
-       u8 mask = 0;
 
-       if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE)
+       if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE) {
                return 0x1f;
-       if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
-               u32 reg = 0;
-               if (isa_dev)
-                       pci_read_config_dword(isa_dev, 0x64, &reg);
-                       
-               /*
-                *      Don't enable UDMA on disk devices for the moment
-                */
-               if(drive->media == ide_disk)
-                       return 0;
-               /* Check the OSB4 DMA33 enable bit */
-               return ((reg & 0x00004000) == 0x00004000) ? 0x07 : 0;
        } else if (dev->revision < SVWKS_CSB5_REVISION_NEW) {
                return 0x07;
-       } else if (dev->revision >= SVWKS_CSB5_REVISION_NEW) {
-               u8 btr = 0, mode;
+       } else {
+               u8 btr = 0, mode, mask;
+
                pci_read_config_byte(dev, 0x5A, &btr);
                mode = btr & 0x3;
 
@@ -101,13 +87,9 @@ static u8 svwks_udma_filter(ide_drive_t *drive)
                case 1:  mask = 0x07; break;
                default: mask = 0x00; break;
                }
-       }
-       if (((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
-            (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) &&
-           (!(PCI_FUNC(dev->devfn) & 1)))
-               mask = 0x1f;
 
-       return mask;
+               return mask;
+       }
 }
 
 static u8 svwks_csb_check (struct pci_dev *dev)
@@ -124,12 +106,13 @@ static u8 svwks_csb_check (struct pci_dev *dev)
        return 0;
 }
 
-static void svwks_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void svwks_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static const u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
        static const u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 };
 
-       struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
+       struct pci_dev *dev = to_pci_dev(hwif->dev);
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        pci_write_config_byte(dev, drive_pci[drive->dn], pio_modes[pio]);
 
@@ -145,14 +128,14 @@ static void svwks_set_pio_mode(ide_drive_t *drive, const u8 pio)
        }
 }
 
-static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void svwks_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static const u8 udma_modes[]            = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
        static const u8 dma_modes[]             = { 0x77, 0x21, 0x20 };
        static const u8 drive_pci2[]            = { 0x45, 0x44, 0x47, 0x46 };
 
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
+       const u8 speed          = drive->dma_mode;
        u8 unit                 = drive->dn & 1;
 
        u8 ultra_enable  = 0, ultra_timing = 0, dma_timing = 0;
@@ -185,8 +168,9 @@ static int init_chipset_svwks(struct pci_dev *dev)
 
        /* OSB4 : South Bridge and IDE */
        if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
-               isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
-                         PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
+               struct pci_dev *isa_dev =
+                       pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
+                                       PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
                if (isa_dev) {
                        pci_read_config_dword(isa_dev, 0x64, &reg);
                        reg &= ~0x00002000; /* disable 600ns interrupt mask */
@@ -195,6 +179,7 @@ static int init_chipset_svwks(struct pci_dev *dev)
                                        "enabled.\n", pci_name(dev));
                        reg |=  0x00004000; /* enable UDMA/33 support */
                        pci_write_config_dword(isa_dev, 0x64, reg);
+                       pci_dev_put(isa_dev);
                }
        }
 
@@ -343,7 +328,6 @@ static u8 svwks_cable_detect(ide_hwif_t *hwif)
 static const struct ide_port_ops osb4_port_ops = {
        .set_pio_mode           = svwks_set_pio_mode,
        .set_dma_mode           = svwks_set_dma_mode,
-       .udma_filter            = svwks_udma_filter,
 };
 
 static const struct ide_port_ops svwks_port_ops = {
@@ -460,6 +444,6 @@ static void __exit svwks_ide_exit(void)
 module_init(svwks_ide_init);
 module_exit(svwks_ide_exit);
 
-MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick");
+MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick, Bartlomiej Zolnierkiewicz");
 MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE");
 MODULE_LICENSE("GPL");
index b7d61dc6409651f370cb02b22f2093244904bb5b..e3ea591f66d36893e7d74ccc14f297ce673f9afd 100644 (file)
@@ -255,7 +255,7 @@ static int sgiioc4_dma_end(ide_drive_t *drive)
        return dma_stat;
 }
 
-static void sgiioc4_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void sgiioc4_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
 }
 
index d95df528562f0db7d1dd0b782954d4a30e7125ef..ddeda444a27aebdcfda34a20bb04742d288656d2 100644 (file)
@@ -229,19 +229,18 @@ static u8 sil_sata_udma_filter(ide_drive_t *drive)
 
 /**
  *     sil_set_pio_mode        -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     Load the timing settings for this device mode into the
  *     controller.
  */
 
-static void sil_set_pio_mode(ide_drive_t *drive, u8 pio)
+static void sil_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static const u16 tf_speed[]   = { 0x328a, 0x2283, 0x1281, 0x10c3, 0x10c1 };
        static const u16 data_speed[] = { 0x328a, 0x2283, 0x1104, 0x10c3, 0x10c1 };
 
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        ide_drive_t *pair       = ide_get_pair_dev(drive);
        u32 speedt              = 0;
@@ -249,6 +248,7 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio)
        unsigned long addr      = siimage_seldev(drive, 0x04);
        unsigned long tfaddr    = siimage_selreg(hwif,  0x02);
        unsigned long base      = (unsigned long)hwif->hwif_data;
+       const u8 pio            = drive->pio_mode - XFER_PIO_0;
        u8 tf_pio               = pio;
        u8 mmio                 = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
        u8 addr_mask            = hwif->channel ? (mmio ? 0xF4 : 0x84)
@@ -258,7 +258,7 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio)
 
        /* trim *taskfile* PIO to the slowest of the master/slave */
        if (pair) {
-               u8 pair_pio = ide_get_best_pio_mode(pair, 255, 4);
+               u8 pair_pio = pair->pio_mode - XFER_PIO_0;
 
                if (pair_pio < tf_pio)
                        tf_pio = pair_pio;
@@ -289,19 +289,18 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio)
 
 /**
  *     sil_set_dma_mode        -       set host controller for DMA mode
+ *     @hwif: port
  *     @drive: drive
- *     @speed: DMA mode
  *
  *     Tune the SiI chipset for the desired DMA mode.
  */
 
-static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void sil_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static const u8 ultra6[] = { 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 };
        static const u8 ultra5[] = { 0x0C, 0x07, 0x05, 0x04, 0x02, 0x01 };
        static const u16 dma[]   = { 0x2208, 0x10C2, 0x10C1 };
 
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        unsigned long base      = (unsigned long)hwif->hwif_data;
        u16 ultra = 0, multi    = 0;
@@ -311,6 +310,7 @@ static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed)
                                                : (mmio ? 0xB4 : 0x80);
        unsigned long ma        = siimage_seldev(drive, 0x08);
        unsigned long ua        = siimage_seldev(drive, 0x0C);
+       const u8 speed          = drive->dma_mode;
 
        scsc  = sil_ioread8 (dev, base + (mmio ? 0x4A : 0x8A));
        mode  = sil_ioread8 (dev, base + addr_mask);
index 468706082fb59ffbf89f11a3eaa139f6d52bfc66..db7f4e761dbc490ee0941a371d37dc926bc90a17 100644 (file)
@@ -290,10 +290,10 @@ static void config_drive_art_rwp(ide_drive_t *drive)
                pci_write_config_byte(dev, 0x4b, rw_prefetch);
 }
 
-static void sis_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void sis_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        config_drive_art_rwp(drive);
-       sis_program_timings(drive, XFER_PIO_0 + pio);
+       sis_program_timings(drive, drive->pio_mode);
 }
 
 static void sis_ata133_program_udma_timings(ide_drive_t *drive, const u8 mode)
@@ -340,8 +340,10 @@ static void sis_program_udma_timings(ide_drive_t *drive, const u8 mode)
                sis_ata33_program_udma_timings(drive, mode);
 }
 
-static void sis_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void sis_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
+       const u8 speed = drive->dma_mode;
+
        if (speed >= XFER_UDMA_0)
                sis_program_udma_timings(drive, speed);
        else
index 3c2bbf0057ea594e68d99cd81542d6443443f397..f21dc2ad768274bc03d1cbe88b487ad0fc87d3e8 100644 (file)
@@ -63,12 +63,13 @@ static unsigned int get_pio_timings(ide_drive_t *drive, u8 pio)
 /*
  * Configure the chipset for PIO mode.
  */
-static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void sl82c105_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       struct pci_dev *dev     = to_pci_dev(drive->hwif->dev);
+       struct pci_dev *dev     = to_pci_dev(hwif->dev);
        unsigned long timings   = (unsigned long)ide_get_drivedata(drive);
        int reg                 = 0x44 + drive->dn * 4;
        u16 drv_ctrl;
+       const u8 pio            = drive->pio_mode - XFER_PIO_0;
 
        drv_ctrl = get_pio_timings(drive, pio);
 
@@ -91,11 +92,12 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
 /*
  * Configure the chipset for DMA mode.
  */
-static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void sl82c105_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
        static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
        unsigned long timings = (unsigned long)ide_get_drivedata(drive);
        u16 drv_ctrl;
+       const u8 speed = drive->dma_mode;
 
        drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0];
 
index 1ccfb40e721511210527fb5b603c81b2eaa9e0f6..864ffe0e26d9eb0342e139bce4e468d25d36d3a7 100644 (file)
@@ -18,9 +18,8 @@
 
 static DEFINE_SPINLOCK(slc90e66_lock);
 
-static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void slc90e66_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        int is_slave            = drive->dn & 1;
        int master_port         = hwif->channel ? 0x42 : 0x40;
@@ -29,6 +28,8 @@ static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio)
        u16 master_data;
        u8 slave_data;
        int control = 0;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
+
                                     /* ISP  RTC */
        static const u8 timings[][2] = {
                                        { 0, 0 },
@@ -71,14 +72,14 @@ static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio)
        spin_unlock_irqrestore(&slc90e66_lock, flags);
 }
 
-static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed)
+static void slc90e66_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        u8 maslave              = hwif->channel ? 0x42 : 0x40;
        int sitre = 0, a_speed  = 7 << (drive->dn * 4);
        int u_speed = 0, u_flag = 1 << drive->dn;
        u16                     reg4042, reg44, reg48, reg4a;
+       const u8 speed          = drive->dma_mode;
 
        pci_read_config_word(dev, maslave, &reg4042);
        sitre = (reg4042 & 0x4000) ? 1 : 0;
@@ -98,7 +99,6 @@ static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed)
                }
        } else {
                const u8 mwdma_to_pio[] = { 0, 3, 4 };
-               u8 pio;
 
                if (reg48 & u_flag)
                        pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
@@ -106,11 +106,12 @@ static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed)
                        pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
 
                if (speed >= XFER_MW_DMA_0)
-                       pio = mwdma_to_pio[speed - XFER_MW_DMA_0];
+                       drive->pio_mode =
+                               mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
                else
-                       pio = 2; /* only SWDMA2 is allowed */
+                       drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */
 
-               slc90e66_set_pio_mode(drive, pio);
+               slc90e66_set_pio_mode(hwif, drive);
        }
 }
 
index 05a93d6baecc60edf3ba587ab26c0947907915aa..e444d24934b350472eead452b377063d4123c1af 100644 (file)
 
 #define DRV_NAME "tc86c001"
 
-static void tc86c001_set_mode(ide_drive_t *drive, const u8 speed)
+static void tc86c001_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif        = drive->hwif;
        unsigned long scr_port  = hwif->config_data + (drive->dn ? 0x02 : 0x00);
        u16 mode, scr           = inw(scr_port);
+       const u8 speed          = drive->dma_mode;
 
        switch (speed) {
        case XFER_UDMA_4:       mode = 0x00c0; break;
@@ -41,9 +41,10 @@ static void tc86c001_set_mode(ide_drive_t *drive, const u8 speed)
        outw(scr, scr_port);
 }
 
-static void tc86c001_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void tc86c001_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       tc86c001_set_mode(drive, XFER_PIO_0 + pio);
+       drive->dma_mode = drive->pio_mode;
+       tc86c001_set_mode(hwif, drive);
 }
 
 /*
index 8773c3ba7462d9f07dfb5bb7a1b2707e3a437972..7953447eae0fb8fcfadd179f2b446c6666e1adc6 100644 (file)
@@ -34,9 +34,8 @@
 
 #define DRV_NAME "triflex"
 
-static void triflex_set_mode(ide_drive_t *drive, const u8 speed)
+static void triflex_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct pci_dev *dev = to_pci_dev(hwif->dev);
        u32 triflex_timings = 0;
        u16 timing = 0;
@@ -44,7 +43,7 @@ static void triflex_set_mode(ide_drive_t *drive, const u8 speed)
 
        pci_read_config_dword(dev, channel_offset, &triflex_timings);
 
-       switch(speed) {
+       switch (drive->dma_mode) {
                case XFER_MW_DMA_2:
                        timing = 0x0103; 
                        break;
@@ -82,9 +81,10 @@ static void triflex_set_mode(ide_drive_t *drive, const u8 speed)
        pci_write_config_dword(dev, channel_offset, triflex_timings);
 }
 
-static void triflex_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void triflex_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       triflex_set_mode(drive, XFER_PIO_0 + pio);
+       drive->dma_mode = drive->pio_mode;
+       triflex_set_mode(hwif, drive);
 }
 
 static const struct ide_port_ops triflex_port_ops = {
index fd59c0d235b56cc0ce04a06a2298a0d03b4d4bb0..1d80f1fdbc973edc18bf77115d4aa612557b3e9e 100644 (file)
@@ -56,16 +56,15 @@ static void tx4938ide_tune_ebusc(unsigned int ebus_ch,
                     &tx4938_ebuscptr->cr[ebus_ch]);
 }
 
-static void tx4938ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void tx4938ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        struct tx4938ide_platform_info *pdata = hwif->dev->platform_data;
-       u8 safe = pio;
+       u8 safe = drive->pio_mode - XFER_PIO_0;
        ide_drive_t *pair;
 
        pair = ide_get_pair_dev(drive);
        if (pair)
-               safe = min(safe, ide_get_best_pio_mode(pair, 255, 5));
+               safe = min(safe, pair->pio_mode - XFER_PIO_0);
        tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, safe);
 }
 
index 64b58ecc3f0ea7131d697634e4c848554f5b7e17..3c73677518733175d6497be954a72f4f08841e9b 100644 (file)
@@ -104,17 +104,17 @@ static void tx4939ide_writeb(u8 val, void __iomem *base, u32 reg)
 
 #define TX4939IDE_BASE(hwif)   ((void __iomem *)(hwif)->extra_base)
 
-static void tx4939ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void tx4939ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        int is_slave = drive->dn;
        u32 mask, val;
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
        u8 safe = pio;
        ide_drive_t *pair;
 
        pair = ide_get_pair_dev(drive);
        if (pair)
-               safe = min(safe, ide_get_best_pio_mode(pair, 255, 4));
+               safe = min(safe, pair->pio_mode - XFER_PIO_0);
        /*
         * Update Command Transfer Mode for master/slave and Data
         * Transfer Mode for this drive.
@@ -125,10 +125,10 @@ static void tx4939ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
        /* tx4939ide_tf_load_fixup() will set the Sys_Ctl register */
 }
 
-static void tx4939ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
+static void tx4939ide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        u32 mask, val;
+       const u8 mode = drive->dma_mode;
 
        /* Update Data Transfer Mode for this drive. */
        if (mode >= XFER_UDMA_0)
index 60f936e2319cb3fef78dab4f05096f535ebe5b72..47adcd09cb26a3c6f867374ec31b9aa6dfb039e1 100644 (file)
@@ -104,10 +104,11 @@ static void umc_set_speeds(u8 speeds[])
                speeds[0], speeds[1], speeds[2], speeds[3]);
 }
 
-static void umc_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void umc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif, *mate = hwif->mate;
+       ide_hwif_t *mate = hwif->mate;
        unsigned long uninitialized_var(flags);
+       const u8 pio = drive->pio_mode - XFER_PIO_0;
 
        printk("%s: setting umc8672 to PIO mode%d (speed %d)\n",
                drive->name, pio, pio_to_umc[pio]);
index 028de26a25feedb323336df57c10039e7b85ab9b..e65d010b708db1fcff3182d7b87a1ed1e7214f06 100644 (file)
@@ -6,7 +6,7 @@
  *   vt8235, vt8237, vt8237a
  *
  * Copyright (c) 2000-2002 Vojtech Pavlik
- * Copyright (c) 2007 Bartlomiej Zolnierkiewicz
+ * Copyright (c) 2007-2010 Bartlomiej Zolnierkiewicz
  *
  * Based on the work of:
  *     Michel Aubry
 #define VIA_NO_UNMASK          0x08 /* Doesn't work with IRQ unmasking on */
 #define VIA_BAD_ID             0x10 /* Has wrong vendor ID (0x1107) */
 #define VIA_BAD_AST            0x20 /* Don't touch Address Setup Timing */
+#define VIA_SATA_PATA          0x80 /* SATA/PATA combined configuration */
+
+enum {
+       VIA_IDFLAG_SINGLE = (1 << 1), /* single channel controller */
+};
 
 /*
  * VIA SouthBridge chips.
@@ -67,11 +72,13 @@ static struct via_isa_bridge {
        u8 udma_mask;
        u8 flags;
 } via_isa_bridges[] = {
-       { "vx855",      PCI_DEVICE_ID_VIA_VX855,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
-       { "vx800",      PCI_DEVICE_ID_VIA_VX800,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
-       { "cx700",      PCI_DEVICE_ID_VIA_CX700,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
+       { "vx855",      PCI_DEVICE_ID_VIA_VX855,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA },
+       { "vx800",      PCI_DEVICE_ID_VIA_VX800,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA },
+       { "cx700",      PCI_DEVICE_ID_VIA_CX700,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA },
+       { "vt8261",     PCI_DEVICE_ID_VIA_8261,     0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
        { "vt8237s",    PCI_DEVICE_ID_VIA_8237S,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
        { "vt6410",     PCI_DEVICE_ID_VIA_6410,     0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
+       { "vt6415",     PCI_DEVICE_ID_VIA_6410,     0x00, 0xff, ATA_UDMA6, VIA_BAD_AST },
        { "vt8251",     PCI_DEVICE_ID_VIA_8251,     0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
        { "vt8237",     PCI_DEVICE_ID_VIA_8237,     0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
        { "vt8237a",    PCI_DEVICE_ID_VIA_8237A,    0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
@@ -92,6 +99,7 @@ static struct via_isa_bridge {
        { "vt82c586",   PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f,      0x00, VIA_SET_FIFO },
        { "vt82c576",   PCI_DEVICE_ID_VIA_82C576,   0x00, 0x2f,      0x00, VIA_SET_FIFO | VIA_NO_UNMASK },
        { "vt82c576",   PCI_DEVICE_ID_VIA_82C576,   0x00, 0x2f,      0x00, VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID },
+       { "vtxxxx",     PCI_DEVICE_ID_VIA_ANON,     0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
        { NULL }
 };
 
@@ -102,6 +110,7 @@ struct via82cxxx_dev
 {
        struct via_isa_bridge *via_config;
        unsigned int via_80w;
+       u8 cached_device[2];
 };
 
 /**
@@ -137,30 +146,45 @@ static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing)
        case ATA_UDMA4: t = timing->udma ? (0xe8 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x0f; break;
        case ATA_UDMA5: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break;
        case ATA_UDMA6: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break;
-       default: return;
        }
 
-       pci_write_config_byte(dev, VIA_UDMA_TIMING + (3 - dn), t);
+       /* Set UDMA unless device is not UDMA capable */
+       if (vdev->via_config->udma_mask) {
+               u8 udma_etc;
+
+               pci_read_config_byte(dev, VIA_UDMA_TIMING + 3 - dn, &udma_etc);
+
+               /* clear transfer mode bit */
+               udma_etc &= ~0x20;
+
+               if (timing->udma) {
+                       /* preserve 80-wire cable detection bit */
+                       udma_etc &= 0x10;
+                       udma_etc |= t;
+               }
+
+               pci_write_config_byte(dev, VIA_UDMA_TIMING + 3 - dn, udma_etc);
+       }
 }
 
 /**
  *     via_set_drive           -       configure transfer mode
+ *     @hwif: port
  *     @drive: Drive to set up
- *     @speed: desired speed
  *
  *     via_set_drive() computes timing values configures the chipset to
  *     a desired transfer mode.  It also can be called by upper layers.
  */
 
-static void via_set_drive(ide_drive_t *drive, const u8 speed)
+static void via_set_drive(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = drive->hwif;
        ide_drive_t *peer = ide_get_pair_dev(drive);
        struct pci_dev *dev = to_pci_dev(hwif->dev);
        struct ide_host *host = pci_get_drvdata(dev);
        struct via82cxxx_dev *vdev = host->host_priv;
        struct ide_timing t, p;
        unsigned int T, UT;
+       const u8 speed = drive->dma_mode;
 
        T = 1000000000 / via_clock;
 
@@ -175,7 +199,7 @@ static void via_set_drive(ide_drive_t *drive, const u8 speed)
        ide_timing_compute(drive, speed, &t, T, UT);
 
        if (peer) {
-               ide_timing_compute(peer, peer->current_speed, &p, T, UT);
+               ide_timing_compute(peer, peer->pio_mode, &p, T, UT);
                ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
        }
 
@@ -184,22 +208,24 @@ static void via_set_drive(ide_drive_t *drive, const u8 speed)
 
 /**
  *     via_set_pio_mode        -       set host controller for PIO mode
+ *     @hwif: port
  *     @drive: drive
- *     @pio: PIO mode number
  *
  *     A callback from the upper layers for PIO-only tuning.
  */
 
-static void via_set_pio_mode(ide_drive_t *drive, const u8 pio)
+static void via_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
 {
-       via_set_drive(drive, XFER_PIO_0 + pio);
+       drive->dma_mode = drive->pio_mode;
+       via_set_drive(hwif, drive);
 }
 
 static struct via_isa_bridge *via_config_find(struct pci_dev **isa)
 {
        struct via_isa_bridge *via_config;
 
-       for (via_config = via_isa_bridges; via_config->id; via_config++)
+       for (via_config = via_isa_bridges;
+            via_config->id != PCI_DEVICE_ID_VIA_ANON; via_config++)
                if ((*isa = pci_get_device(PCI_VENDOR_ID_VIA +
                        !!(via_config->flags & VIA_BAD_ID),
                        via_config->id, NULL))) {
@@ -362,6 +388,9 @@ static u8 via82cxxx_cable_detect(ide_hwif_t *hwif)
        if (via_cable_override(pdev))
                return ATA_CBL_PATA40_SHORT;
 
+       if ((vdev->via_config->flags & VIA_SATA_PATA) && hwif->channel == 0)
+               return ATA_CBL_SATA;
+
        if ((vdev->via_80w >> hwif->channel) & 1)
                return ATA_CBL_PATA80;
        else
@@ -374,10 +403,66 @@ static const struct ide_port_ops via_port_ops = {
        .cable_detect           = via82cxxx_cable_detect,
 };
 
+static void via_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+       struct via82cxxx_dev *vdev = hwif->host->host_priv;
+
+       outb(ctl, hwif->io_ports.ctl_addr);
+       outb(vdev->cached_device[hwif->channel], hwif->io_ports.device_addr);
+}
+
+static void __via_dev_select(ide_drive_t *drive, u8 select)
+{
+       ide_hwif_t *hwif = drive->hwif;
+       struct via82cxxx_dev *vdev = hwif->host->host_priv;
+
+       outb(select, hwif->io_ports.device_addr);
+       vdev->cached_device[hwif->channel] = select;
+}
+
+static void via_dev_select(ide_drive_t *drive)
+{
+       __via_dev_select(drive, drive->select | ATA_DEVICE_OBS);
+}
+
+static void via_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
+{
+       ide_hwif_t *hwif = drive->hwif;
+       struct ide_io_ports *io_ports = &hwif->io_ports;
+
+       if (valid & IDE_VALID_FEATURE)
+               outb(tf->feature, io_ports->feature_addr);
+       if (valid & IDE_VALID_NSECT)
+               outb(tf->nsect, io_ports->nsect_addr);
+       if (valid & IDE_VALID_LBAL)
+               outb(tf->lbal, io_ports->lbal_addr);
+       if (valid & IDE_VALID_LBAM)
+               outb(tf->lbam, io_ports->lbam_addr);
+       if (valid & IDE_VALID_LBAH)
+               outb(tf->lbah, io_ports->lbah_addr);
+       if (valid & IDE_VALID_DEVICE)
+               __via_dev_select(drive, tf->device);
+}
+
+const struct ide_tp_ops via_tp_ops = {
+       .exec_command           = ide_exec_command,
+       .read_status            = ide_read_status,
+       .read_altstatus         = ide_read_altstatus,
+       .write_devctl           = via_write_devctl,
+
+       .dev_select             = via_dev_select,
+       .tf_load                = via_tf_load,
+       .tf_read                = ide_tf_read,
+
+       .input_data             = ide_input_data,
+       .output_data            = ide_output_data,
+};
+
 static const struct ide_port_info via82cxxx_chipset __devinitdata = {
        .name           = DRV_NAME,
        .init_chipset   = init_chipset_via82cxxx,
        .enablebits     = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } },
+       .tp_ops         = &via_tp_ops,
        .port_ops       = &via_port_ops,
        .host_flags     = IDE_HFLAG_PIO_NO_BLACKLIST |
                          IDE_HFLAG_POST_SET_MODE |
@@ -402,11 +487,6 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i
         * Find the ISA bridge and check we know what it is.
         */
        via_config = via_config_find(&isa);
-       if (!via_config->id) {
-               printk(KERN_WARNING DRV_NAME " %s: unknown chipset, skipping\n",
-                       pci_name(dev));
-               return -ENODEV;
-       }
 
        /*
         * Print the boot message.
@@ -436,10 +516,13 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i
                via_clock = 33333;
        }
 
-       if (idx == 0)
-               d.host_flags |= IDE_HFLAG_NO_AUTODMA;
-       else
+       if (idx == 1)
                d.enablebits[1].reg = d.enablebits[0].reg = 0;
+       else
+               d.host_flags |= IDE_HFLAG_NO_AUTODMA;
+
+       if (idx == VIA_IDFLAG_SINGLE)
+               d.host_flags |= IDE_HFLAG_SINGLE;
 
        if ((via_config->flags & VIA_NO_UNMASK) == 0)
                d.host_flags |= IDE_HFLAG_UNMASK_IRQS;
@@ -475,8 +558,9 @@ static const struct pci_device_id via_pci_tbl[] = {
        { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1),  0 },
        { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C586_1),  0 },
        { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_CX700_IDE), 0 },
-       { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_VX855_IDE), 0 },
+       { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_VX855_IDE), VIA_IDFLAG_SINGLE },
        { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410),      1 },
+       { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6415),      1 },
        { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), 1 },
        { 0, },
 };
@@ -504,6 +588,6 @@ static void __exit via_ide_exit(void)
 module_init(via_ide_init);
 module_exit(via_ide_exit);
 
-MODULE_AUTHOR("Vojtech Pavlik, Michel Aubry, Jeff Garzik, Andre Hedrick");
+MODULE_AUTHOR("Vojtech Pavlik, Bartlomiej Zolnierkiewicz, Michel Aubry, Jeff Garzik, Andre Hedrick");
 MODULE_DESCRIPTION("PCI driver module for VIA IDE");
 MODULE_LICENSE("GPL");
index 7522008fda86880f759128e694d9df0cda68c764..58463da814d1c671272df2ab4f778986ef84f4dc 100644 (file)
@@ -1193,10 +1193,7 @@ static int method_in_use(struct ib_mad_mgmt_method_table **method,
 {
        int i;
 
-       for (i = find_first_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS);
-            i < IB_MGMT_MAX_METHODS;
-            i = find_next_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS,
-                              1+i)) {
+       for_each_set_bit(i, mad_reg_req->method_mask, IB_MGMT_MAX_METHODS) {
                if ((*method)->agent[i]) {
                        printk(KERN_ERR PFX "Method %d already in use\n", i);
                        return -EINVAL;
@@ -1330,13 +1327,9 @@ static int add_nonoui_reg_req(struct ib_mad_reg_req *mad_reg_req,
                goto error3;
 
        /* Finally, add in methods being registered */
-       for (i = find_first_bit(mad_reg_req->method_mask,
-                               IB_MGMT_MAX_METHODS);
-            i < IB_MGMT_MAX_METHODS;
-            i = find_next_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS,
-                              1+i)) {
+       for_each_set_bit(i, mad_reg_req->method_mask, IB_MGMT_MAX_METHODS)
                (*method)->agent[i] = agent_priv;
-       }
+
        return 0;
 
 error3:
@@ -1429,13 +1422,9 @@ check_in_use:
                goto error4;
 
        /* Finally, add in methods being registered */
-       for (i = find_first_bit(mad_reg_req->method_mask,
-                               IB_MGMT_MAX_METHODS);
-            i < IB_MGMT_MAX_METHODS;
-            i = find_next_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS,
-                              1+i)) {
+       for_each_set_bit(i, mad_reg_req->method_mask, IB_MGMT_MAX_METHODS)
                (*method)->agent[i] = agent_priv;
-       }
+
        return 0;
 
 error4:
index e54d9ac6d1cabee6a14e25dc5ce94939d5d2910e..a078e5624d22f700c0a526538e6e2926f5d2775f 100644 (file)
@@ -146,7 +146,7 @@ extern struct idr ib_uverbs_srq_idr;
 void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj);
 
 struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
-                                       int is_async, int *fd);
+                                       int is_async);
 struct ib_uverbs_event_file *ib_uverbs_lookup_comp_file(int fd);
 
 void ib_uverbs_release_ucq(struct ib_uverbs_file *file,
index 112d3970222a08c3ea43af3aeeb4c9977c5546ff..f71cf138d674109c6d3f10381f64d4ffcb8c5358 100644 (file)
@@ -301,10 +301,15 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
 
        resp.num_comp_vectors = file->device->num_comp_vectors;
 
-       filp = ib_uverbs_alloc_event_file(file, 1, &resp.async_fd);
+       ret = get_unused_fd();
+       if (ret < 0)
+               goto err_free;
+       resp.async_fd = ret;
+
+       filp = ib_uverbs_alloc_event_file(file, 1);
        if (IS_ERR(filp)) {
                ret = PTR_ERR(filp);
-               goto err_free;
+               goto err_fd;
        }
 
        if (copy_to_user((void __user *) (unsigned long) cmd.response,
@@ -332,9 +337,11 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
        return in_len;
 
 err_file:
-       put_unused_fd(resp.async_fd);
        fput(filp);
 
+err_fd:
+       put_unused_fd(resp.async_fd);
+
 err_free:
        ibdev->dealloc_ucontext(ucontext);
 
@@ -715,6 +722,7 @@ ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,
        struct ib_uverbs_create_comp_channel       cmd;
        struct ib_uverbs_create_comp_channel_resp  resp;
        struct file                               *filp;
+       int ret;
 
        if (out_len < sizeof resp)
                return -ENOSPC;
@@ -722,9 +730,16 @@ ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,
        if (copy_from_user(&cmd, buf, sizeof cmd))
                return -EFAULT;
 
-       filp = ib_uverbs_alloc_event_file(file, 0, &resp.fd);
-       if (IS_ERR(filp))
+       ret = get_unused_fd();
+       if (ret < 0)
+               return ret;
+       resp.fd = ret;
+
+       filp = ib_uverbs_alloc_event_file(file, 0);
+       if (IS_ERR(filp)) {
+               put_unused_fd(resp.fd);
                return PTR_ERR(filp);
+       }
 
        if (copy_to_user((void __user *) (unsigned long) cmd.response,
                         &resp, sizeof resp)) {
index ff59a795e840f7665f8e0b8fca5fdd5b06a31e57..4fa2e65164418e033e36065baeec38d74db32032 100644 (file)
@@ -484,11 +484,10 @@ void ib_uverbs_event_handler(struct ib_event_handler *handler,
 }
 
 struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
-                                       int is_async, int *fd)
+                                       int is_async)
 {
        struct ib_uverbs_event_file *ev_file;
        struct file *filp;
-       int ret;
 
        ev_file = kmalloc(sizeof *ev_file, GFP_KERNEL);
        if (!ev_file)
@@ -503,27 +502,12 @@ struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
        ev_file->is_async    = is_async;
        ev_file->is_closed   = 0;
 
-       *fd = get_unused_fd();
-       if (*fd < 0) {
-               ret = *fd;
-               goto err;
-       }
-
-       filp = anon_inode_getfile("[uverbs-event]", &uverbs_event_fops,
+       filp = anon_inode_getfile("[infinibandevent]", &uverbs_event_fops,
                                  ev_file, O_RDONLY);
-       if (!filp) {
-               ret = -ENFILE;
-               goto err_fd;
-       }
+       if (IS_ERR(filp))
+               kfree(ev_file);
 
        return filp;
-
-err_fd:
-       put_unused_fd(*fd);
-
-err:
-       kfree(ev_file);
-       return ERR_PTR(ret);
 }
 
 /*
index be115b3b65eb45e13bbc5a650a770f13cfb5767c..be54fd639acaedad630ff0ab0343c44d9d29ea86 100644 (file)
@@ -44,7 +44,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data)
 {
        struct mc13783_ts_priv *priv = data;
 
-       mc13783_ackirq(priv->mc13783, irq);
+       mc13783_irq_ack(priv->mc13783, irq);
 
        /*
         * Kick off reading coordinates. Note that if work happens already
@@ -135,7 +135,7 @@ static int mc13783_ts_open(struct input_dev *dev)
 
        mc13783_lock(priv->mc13783);
 
-       mc13783_ackirq(priv->mc13783, MC13783_IRQ_TS);
+       mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TS);
 
        ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_TS,
                mc13783_ts_handler, MC13783_TS_NAME, priv);
index a93637223c8d287f6ff41ecf1de0e1095dbecb29..3bdbb6115702500498548c1936c9fe19510aa414 100644 (file)
@@ -1160,8 +1160,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
        cc->start = tmpll;
 
-       if (dm_get_device(ti, argv[3], cc->start, ti->len,
-                         dm_table_get_mode(ti->table), &cc->dev)) {
+       if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), &cc->dev)) {
                ti->error = "Device lookup failed";
                goto bad_device;
        }
index ebe7381f47c8fd5b1d86cd4e603acad20eebe98f..852052880d7a3b5574b3a718325678d201033506 100644 (file)
@@ -156,8 +156,8 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad;
        }
 
-       if (dm_get_device(ti, argv[0], dc->start_read, ti->len,
-                         dm_table_get_mode(ti->table), &dc->dev_read)) {
+       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
+                         &dc->dev_read)) {
                ti->error = "Device lookup failed";
                goto bad;
        }
@@ -177,8 +177,8 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad_dev_read;
        }
 
-       if (dm_get_device(ti, argv[3], dc->start_write, ti->len,
-                         dm_table_get_mode(ti->table), &dc->dev_write)) {
+       if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table),
+                         &dc->dev_write)) {
                ti->error = "Write device lookup failed";
                goto bad_dev_read;
        }
index 1d669322b27c7fef59d6e92ba32b2b1d79aedc50..d7500e1c26f24dc878bff7bfde6c16d1ac872245 100644 (file)
@@ -285,7 +285,8 @@ retry:
        up_write(&_hash_lock);
 }
 
-static int dm_hash_rename(uint32_t cookie, const char *old, const char *new)
+static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old,
+                         const char *new)
 {
        char *new_name, *old_name;
        struct hash_cell *hc;
@@ -344,7 +345,8 @@ static int dm_hash_rename(uint32_t cookie, const char *old, const char *new)
                dm_table_put(table);
        }
 
-       dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie);
+       if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie))
+               *flags |= DM_UEVENT_GENERATED_FLAG;
 
        dm_put(hc->md);
        up_write(&_hash_lock);
@@ -736,10 +738,10 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
        __hash_remove(hc);
        up_write(&_hash_lock);
 
-       dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr);
+       if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr))
+               param->flags |= DM_UEVENT_GENERATED_FLAG;
 
        dm_put(md);
-       param->data_size = 0;
        return 0;
 }
 
@@ -773,7 +775,9 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size)
                return r;
 
        param->data_size = 0;
-       return dm_hash_rename(param->event_nr, param->name, new_name);
+
+       return dm_hash_rename(param->event_nr, &param->flags, param->name,
+                             new_name);
 }
 
 static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
@@ -897,16 +901,17 @@ static int do_resume(struct dm_ioctl *param)
                        set_disk_ro(dm_disk(md), 1);
        }
 
-       if (dm_suspended_md(md))
+       if (dm_suspended_md(md)) {
                r = dm_resume(md);
+               if (!r && !dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr))
+                       param->flags |= DM_UEVENT_GENERATED_FLAG;
+       }
 
        if (old_map)
                dm_table_destroy(old_map);
 
-       if (!r) {
-               dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr);
+       if (!r)
                r = __dev_status(md, param);
-       }
 
        dm_put(md);
        return r;
@@ -1476,6 +1481,7 @@ static int validate_params(uint cmd, struct dm_ioctl *param)
 {
        /* Always clear this flag */
        param->flags &= ~DM_BUFFER_FULL_FLAG;
+       param->flags &= ~DM_UEVENT_GENERATED_FLAG;
 
        /* Ignores parameters */
        if (cmd == DM_REMOVE_ALL_CMD ||
index 82f7d6e6b1eab551588ae6b8e917622151c4d4ff..9200dbf2391a7934c11314fe3e91ce90275852f4 100644 (file)
@@ -47,8 +47,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
        lc->start = tmp;
 
-       if (dm_get_device(ti, argv[0], lc->start, ti->len,
-                         dm_table_get_mode(ti->table), &lc->dev)) {
+       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev)) {
                ti->error = "dm-linear: Device lookup failed";
                goto bad;
        }
index 7035582786fbd93de70855febce53aac1d283e09..5a08be0222dbee7f27e89942c6ef4458cbcf633a 100644 (file)
@@ -543,8 +543,7 @@ static int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti,
                return -EINVAL;
        }
 
-       r = dm_get_device(ti, argv[0], 0, 0 /* FIXME */,
-                         FMODE_READ | FMODE_WRITE, &dev);
+       r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &dev);
        if (r)
                return r;
 
index e81345a1d08fa2f1fec21437c420ccacbbdcd360..826bce7343b3697e5abdfd4c62e760250f6ecf3a 100644 (file)
@@ -69,6 +69,7 @@ struct multipath {
        struct list_head priority_groups;
        unsigned pg_init_required;      /* pg_init needs calling? */
        unsigned pg_init_in_progress;   /* Only one pg_init allowed at once */
+       wait_queue_head_t pg_init_wait; /* Wait for pg_init completion */
 
        unsigned nr_valid_paths;        /* Total number of usable paths */
        struct pgpath *current_pgpath;
@@ -95,8 +96,6 @@ struct multipath {
        mempool_t *mpio_pool;
 
        struct mutex work_mutex;
-
-       unsigned suspended;     /* Don't create new I/O internally when set. */
 };
 
 /*
@@ -202,6 +201,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
                m->queue_io = 1;
                INIT_WORK(&m->process_queued_ios, process_queued_ios);
                INIT_WORK(&m->trigger_event, trigger_event);
+               init_waitqueue_head(&m->pg_init_wait);
                mutex_init(&m->work_mutex);
                m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
                if (!m->mpio_pool) {
@@ -235,6 +235,21 @@ static void free_multipath(struct multipath *m)
  * Path selection
  *-----------------------------------------------*/
 
+static void __pg_init_all_paths(struct multipath *m)
+{
+       struct pgpath *pgpath;
+
+       m->pg_init_count++;
+       m->pg_init_required = 0;
+       list_for_each_entry(pgpath, &m->current_pg->pgpaths, list) {
+               /* Skip failed paths */
+               if (!pgpath->is_active)
+                       continue;
+               if (queue_work(kmpath_handlerd, &pgpath->activate_path))
+                       m->pg_init_in_progress++;
+       }
+}
+
 static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
 {
        m->current_pg = pgpath->pg;
@@ -439,7 +454,7 @@ static void process_queued_ios(struct work_struct *work)
 {
        struct multipath *m =
                container_of(work, struct multipath, process_queued_ios);
-       struct pgpath *pgpath = NULL, *tmp;
+       struct pgpath *pgpath = NULL;
        unsigned must_queue = 1;
        unsigned long flags;
 
@@ -457,14 +472,9 @@ static void process_queued_ios(struct work_struct *work)
            (!pgpath && !m->queue_if_no_path))
                must_queue = 0;
 
-       if (m->pg_init_required && !m->pg_init_in_progress && pgpath) {
-               m->pg_init_count++;
-               m->pg_init_required = 0;
-               list_for_each_entry(tmp, &pgpath->pg->pgpaths, list) {
-                       if (queue_work(kmpath_handlerd, &tmp->activate_path))
-                               m->pg_init_in_progress++;
-               }
-       }
+       if (m->pg_init_required && !m->pg_init_in_progress && pgpath)
+               __pg_init_all_paths(m);
+
 out:
        spin_unlock_irqrestore(&m->lock, flags);
        if (!must_queue)
@@ -597,8 +607,8 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
        if (!p)
                return ERR_PTR(-ENOMEM);
 
-       r = dm_get_device(ti, shift(as), ti->begin, ti->len,
-                         dm_table_get_mode(ti->table), &p->path.dev);
+       r = dm_get_device(ti, shift(as), dm_table_get_mode(ti->table),
+                         &p->path.dev);
        if (r) {
                ti->error = "error getting device";
                goto bad;
@@ -890,9 +900,34 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
        return r;
 }
 
-static void flush_multipath_work(void)
+static void multipath_wait_for_pg_init_completion(struct multipath *m)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long flags;
+
+       add_wait_queue(&m->pg_init_wait, &wait);
+
+       while (1) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+
+               spin_lock_irqsave(&m->lock, flags);
+               if (!m->pg_init_in_progress) {
+                       spin_unlock_irqrestore(&m->lock, flags);
+                       break;
+               }
+               spin_unlock_irqrestore(&m->lock, flags);
+
+               io_schedule();
+       }
+       set_current_state(TASK_RUNNING);
+
+       remove_wait_queue(&m->pg_init_wait, &wait);
+}
+
+static void flush_multipath_work(struct multipath *m)
 {
        flush_workqueue(kmpath_handlerd);
+       multipath_wait_for_pg_init_completion(m);
        flush_workqueue(kmultipathd);
        flush_scheduled_work();
 }
@@ -901,7 +936,7 @@ static void multipath_dtr(struct dm_target *ti)
 {
        struct multipath *m = ti->private;
 
-       flush_multipath_work();
+       flush_multipath_work(m);
        free_multipath(m);
 }
 
@@ -1128,8 +1163,7 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
 
 static void pg_init_done(void *data, int errors)
 {
-       struct dm_path *path = data;
-       struct pgpath *pgpath = path_to_pgpath(path);
+       struct pgpath *pgpath = data;
        struct priority_group *pg = pgpath->pg;
        struct multipath *m = pg->m;
        unsigned long flags;
@@ -1143,8 +1177,8 @@ static void pg_init_done(void *data, int errors)
                        errors = 0;
                        break;
                }
-               DMERR("Cannot failover device because scsi_dh_%s was not "
-                     "loaded.", m->hw_handler_name);
+               DMERR("Could not failover the device: Handler scsi_dh_%s "
+                     "Error %d.", m->hw_handler_name, errors);
                /*
                 * Fail path for now, so we do not ping pong
                 */
@@ -1181,14 +1215,24 @@ static void pg_init_done(void *data, int errors)
                        m->current_pgpath = NULL;
                        m->current_pg = NULL;
                }
-       } else if (!m->pg_init_required) {
-               m->queue_io = 0;
+       } else if (!m->pg_init_required)
                pg->bypassed = 0;
-       }
 
-       m->pg_init_in_progress--;
-       if (!m->pg_init_in_progress)
-               queue_work(kmultipathd, &m->process_queued_ios);
+       if (--m->pg_init_in_progress)
+               /* Activations of other paths are still on going */
+               goto out;
+
+       if (!m->pg_init_required)
+               m->queue_io = 0;
+
+       queue_work(kmultipathd, &m->process_queued_ios);
+
+       /*
+        * Wake up any thread waiting to suspend.
+        */
+       wake_up(&m->pg_init_wait);
+
+out:
        spin_unlock_irqrestore(&m->lock, flags);
 }
 
@@ -1198,7 +1242,7 @@ static void activate_path(struct work_struct *work)
                container_of(work, struct pgpath, activate_path);
 
        scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
-                               pg_init_done, &pgpath->path);
+                               pg_init_done, pgpath);
 }
 
 /*
@@ -1276,8 +1320,7 @@ static void multipath_postsuspend(struct dm_target *ti)
        struct multipath *m = ti->private;
 
        mutex_lock(&m->work_mutex);
-       m->suspended = 1;
-       flush_multipath_work();
+       flush_multipath_work(m);
        mutex_unlock(&m->work_mutex);
 }
 
@@ -1289,10 +1332,6 @@ static void multipath_resume(struct dm_target *ti)
        struct multipath *m = (struct multipath *) ti->private;
        unsigned long flags;
 
-       mutex_lock(&m->work_mutex);
-       m->suspended = 0;
-       mutex_unlock(&m->work_mutex);
-
        spin_lock_irqsave(&m->lock, flags);
        m->queue_if_no_path = m->saved_queue_if_no_path;
        spin_unlock_irqrestore(&m->lock, flags);
@@ -1428,11 +1467,6 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
 
        mutex_lock(&m->work_mutex);
 
-       if (m->suspended) {
-               r = -EBUSY;
-               goto out;
-       }
-
        if (dm_suspended(ti)) {
                r = -EBUSY;
                goto out;
@@ -1471,8 +1505,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
                goto out;
        }
 
-       r = dm_get_device(ti, argv[1], ti->begin, ti->len,
-                         dm_table_get_mode(ti->table), &dev);
+       r = dm_get_device(ti, argv[1], dm_table_get_mode(ti->table), &dev);
        if (r) {
                DMWARN("message: error getting device %s",
                       argv[1]);
index 6c1046df81f6f7738cd74eec6a6d5558a8e23f13..ddda531723dcecd5e53d3dc644e41743dc6379a8 100644 (file)
@@ -465,9 +465,17 @@ static void map_region(struct dm_io_region *io, struct mirror *m,
 static void hold_bio(struct mirror_set *ms, struct bio *bio)
 {
        /*
-        * If device is suspended, complete the bio.
+        * Lock is required to avoid race condition during suspend
+        * process.
         */
+       spin_lock_irq(&ms->lock);
+
        if (atomic_read(&ms->suspend)) {
+               spin_unlock_irq(&ms->lock);
+
+               /*
+                * If device is suspended, complete the bio.
+                */
                if (dm_noflush_suspending(ms->ti))
                        bio_endio(bio, DM_ENDIO_REQUEUE);
                else
@@ -478,7 +486,6 @@ static void hold_bio(struct mirror_set *ms, struct bio *bio)
        /*
         * Hold bio until the suspend is complete.
         */
-       spin_lock_irq(&ms->lock);
        bio_list_add(&ms->holds, bio);
        spin_unlock_irq(&ms->lock);
 }
@@ -737,9 +744,12 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
                dm_rh_delay(ms->rh, bio);
 
        while ((bio = bio_list_pop(&nosync))) {
-               if (unlikely(ms->leg_failure) && errors_handled(ms))
-                       hold_bio(ms, bio);
-               else {
+               if (unlikely(ms->leg_failure) && errors_handled(ms)) {
+                       spin_lock_irq(&ms->lock);
+                       bio_list_add(&ms->failures, bio);
+                       spin_unlock_irq(&ms->lock);
+                       wakeup_mirrord(ms);
+               } else {
                        map_bio(get_default_mirror(ms), bio);
                        generic_make_request(bio);
                }
@@ -917,8 +927,7 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
                return -EINVAL;
        }
 
-       if (dm_get_device(ti, argv[0], offset, ti->len,
-                         dm_table_get_mode(ti->table),
+       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
                          &ms->mirror[mirror].dev)) {
                ti->error = "Device lookup failure";
                return -ENXIO;
@@ -1258,6 +1267,20 @@ static void mirror_presuspend(struct dm_target *ti)
 
        atomic_set(&ms->suspend, 1);
 
+       /*
+        * Process bios in the hold list to start recovery waiting
+        * for bios in the hold list. After the process, no bio has
+        * a chance to be added in the hold list because ms->suspend
+        * is set.
+        */
+       spin_lock_irq(&ms->lock);
+       holds = ms->holds;
+       bio_list_init(&ms->holds);
+       spin_unlock_irq(&ms->lock);
+
+       while ((bio = bio_list_pop(&holds)))
+               hold_bio(ms, bio);
+
        /*
         * We must finish up all the work that we've
         * generated (i.e. recovery work).
@@ -1278,22 +1301,6 @@ static void mirror_presuspend(struct dm_target *ti)
         * we know that all of our I/O has been pushed.
         */
        flush_workqueue(ms->kmirrord_wq);
-
-       /*
-        * Now set ms->suspend is set and the workqueue flushed, no more
-        * entries can be added to ms->hold list, so process it.
-        *
-        * Bios can still arrive concurrently with or after this
-        * presuspend function, but they cannot join the hold list
-        * because ms->suspend is set.
-        */
-       spin_lock_irq(&ms->lock);
-       holds = ms->holds;
-       bio_list_init(&ms->holds);
-       spin_unlock_irq(&ms->lock);
-
-       while ((bio = bio_list_pop(&holds)))
-               hold_bio(ms, bio);
 }
 
 static void mirror_postsuspend(struct dm_target *ti)
index ee8eb283650d6ccbe3a5a6e3d6a39ba347d6c35c..54853773510c41216c263c5096164559df7782b8 100644 (file)
@@ -83,10 +83,10 @@ struct dm_snapshot {
        /* Whether or not owning mapped_device is suspended */
        int suspended;
 
-       mempool_t *pending_pool;
-
        atomic_t pending_exceptions_count;
 
+       mempool_t *pending_pool;
+
        struct dm_exception_table pending;
        struct dm_exception_table complete;
 
@@ -96,6 +96,11 @@ struct dm_snapshot {
         */
        spinlock_t pe_lock;
 
+       /* Chunks with outstanding reads */
+       spinlock_t tracked_chunk_lock;
+       mempool_t *tracked_chunk_pool;
+       struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
+
        /* The on disk metadata handler */
        struct dm_exception_store *store;
 
@@ -105,10 +110,12 @@ struct dm_snapshot {
        struct bio_list queued_bios;
        struct work_struct queued_bios_work;
 
-       /* Chunks with outstanding reads */
-       mempool_t *tracked_chunk_pool;
-       spinlock_t tracked_chunk_lock;
-       struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
+       /* Wait for events based on state_bits */
+       unsigned long state_bits;
+
+       /* Range of chunks currently being merged. */
+       chunk_t first_merging_chunk;
+       int num_merging_chunks;
 
        /*
         * The merge operation failed if this flag is set.
@@ -125,13 +132,6 @@ struct dm_snapshot {
         */
        int merge_failed;
 
-       /* Wait for events based on state_bits */
-       unsigned long state_bits;
-
-       /* Range of chunks currently being merged. */
-       chunk_t first_merging_chunk;
-       int num_merging_chunks;
-
        /*
         * Incoming bios that overlap with chunks being merged must wait
         * for them to be committed.
@@ -1081,8 +1081,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        argv++;
        argc--;
 
-       r = dm_get_device(ti, cow_path, 0, 0,
-                         FMODE_READ | FMODE_WRITE, &s->cow);
+       r = dm_get_device(ti, cow_path, FMODE_READ | FMODE_WRITE, &s->cow);
        if (r) {
                ti->error = "Cannot get COW device";
                goto bad_cow;
@@ -1098,7 +1097,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        argv += args_used;
        argc -= args_used;
 
-       r = dm_get_device(ti, origin_path, 0, ti->len, origin_mode, &s->origin);
+       r = dm_get_device(ti, origin_path, origin_mode, &s->origin);
        if (r) {
                ti->error = "Cannot get origin device";
                goto bad_origin;
@@ -2100,8 +2099,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                return -EINVAL;
        }
 
-       r = dm_get_device(ti, argv[0], 0, ti->len,
-                         dm_table_get_mode(ti->table), &dev);
+       r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dev);
        if (r) {
                ti->error = "Cannot get target device";
                return r;
index bd58703ee8f6b83d2a245328e9f30fa5e47dfcf4..e610725db766f643feb7c5c7b932e2c6562e5640 100644 (file)
@@ -80,8 +80,7 @@ static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
        if (sscanf(argv[1], "%llu", &start) != 1)
                return -EINVAL;
 
-       if (dm_get_device(ti, argv[0], start, sc->stripe_width,
-                         dm_table_get_mode(ti->table),
+       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
                          &sc->stripe[stripe].dev))
                return -ENXIO;
 
index 4b22feb01a0c6ced4d850c2e0feba0226d72292f..9924ea23032d6f7418f15f1f8b4198b5c5dedd88 100644 (file)
@@ -429,8 +429,7 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
  * it's already present.
  */
 static int __table_get_device(struct dm_table *t, struct dm_target *ti,
-                             const char *path, sector_t start, sector_t len,
-                             fmode_t mode, struct dm_dev **result)
+                     const char *path, fmode_t mode, struct dm_dev **result)
 {
        int r;
        dev_t uninitialized_var(dev);
@@ -527,11 +526,10 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
 }
 EXPORT_SYMBOL_GPL(dm_set_device_limits);
 
-int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
-                 sector_t len, fmode_t mode, struct dm_dev **result)
+int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+                 struct dm_dev **result)
 {
-       return __table_get_device(ti->table, ti, path,
-                                 start, len, mode, result);
+       return __table_get_device(ti->table, ti, path, mode, result);
 }
 
 
@@ -1231,8 +1229,6 @@ void dm_table_unplug_all(struct dm_table *t)
 
 struct mapped_device *dm_table_get_md(struct dm_table *t)
 {
-       dm_get(t->md);
-
        return t->md;
 }
 
index c7c555a8c7b2ef619ab4671f023a6eb1b8953f33..6b1e3b61b25eacc469fe17e586c45d7f20687dfe 100644 (file)
@@ -187,7 +187,7 @@ void dm_path_uevent(enum dm_uevent_type event_type, struct dm_target *ti,
 
        if (event_type >= ARRAY_SIZE(_dm_uevent_type_names)) {
                DMERR("%s: Invalid event_type %d", __func__, event_type);
-               goto out;
+               return;
        }
 
        event = dm_build_path_uevent(md, ti,
@@ -195,12 +195,9 @@ void dm_path_uevent(enum dm_uevent_type event_type, struct dm_target *ti,
                                     _dm_uevent_type_names[event_type].name,
                                     path, nr_valid_paths);
        if (IS_ERR(event))
-               goto out;
+               return;
 
        dm_uevent_add(md, &event->elist);
-
-out:
-       dm_put(md);
 }
 EXPORT_SYMBOL_GPL(dm_path_uevent);
 
index aa4e2aa86d490c5aa1af423d23c176ed69c25302..d21e1284604f6a2e391ce37c8a6c85a1b06c91ca 100644 (file)
@@ -635,8 +635,10 @@ static void dec_pending(struct dm_io *io, int error)
                        if (!md->barrier_error && io_error != -EOPNOTSUPP)
                                md->barrier_error = io_error;
                        end_io_acct(io);
+                       free_io(md, io);
                } else {
                        end_io_acct(io);
+                       free_io(md, io);
 
                        if (io_error != DM_ENDIO_REQUEUE) {
                                trace_block_bio_complete(md->queue, bio);
@@ -644,8 +646,6 @@ static void dec_pending(struct dm_io *io, int error)
                                bio_endio(bio, io_error);
                        }
                }
-
-               free_io(md, io);
        }
 }
 
@@ -2618,18 +2618,19 @@ out:
 /*-----------------------------------------------------------------
  * Event notification.
  *---------------------------------------------------------------*/
-void dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
+int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
                       unsigned cookie)
 {
        char udev_cookie[DM_COOKIE_LENGTH];
        char *envp[] = { udev_cookie, NULL };
 
        if (!cookie)
-               kobject_uevent(&disk_to_dev(md->disk)->kobj, action);
+               return kobject_uevent(&disk_to_dev(md->disk)->kobj, action);
        else {
                snprintf(udev_cookie, DM_COOKIE_LENGTH, "%s=%u",
                         DM_COOKIE_ENV_VAR_NAME, cookie);
-               kobject_uevent_env(&disk_to_dev(md->disk)->kobj, action, envp);
+               return kobject_uevent_env(&disk_to_dev(md->disk)->kobj,
+                                         action, envp);
        }
 }
 
@@ -2699,23 +2700,13 @@ int dm_suspended_md(struct mapped_device *md)
 
 int dm_suspended(struct dm_target *ti)
 {
-       struct mapped_device *md = dm_table_get_md(ti->table);
-       int r = dm_suspended_md(md);
-
-       dm_put(md);
-
-       return r;
+       return dm_suspended_md(dm_table_get_md(ti->table));
 }
 EXPORT_SYMBOL_GPL(dm_suspended);
 
 int dm_noflush_suspending(struct dm_target *ti)
 {
-       struct mapped_device *md = dm_table_get_md(ti->table);
-       int r = __noflush_suspending(md);
-
-       dm_put(md);
-
-       return r;
+       return __noflush_suspending(dm_table_get_md(ti->table));
 }
 EXPORT_SYMBOL_GPL(dm_noflush_suspending);
 
index 8dadaa5bc39648e58c38d0c31da2872cf0d5f012..bad1724d4869606902740410898d3dfa0e09b1a4 100644 (file)
@@ -125,8 +125,8 @@ void dm_stripe_exit(void);
 int dm_open_count(struct mapped_device *md);
 int dm_lock_for_deletion(struct mapped_device *md);
 
-void dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
-                      unsigned cookie);
+int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
+                     unsigned cookie);
 
 int dm_io_init(void);
 void dm_io_exit(void);
index aa266e1f69b255875ee5b98e67988b31019fcf18..addb846c1e34940e59400a1bb2b9f7a09471c161 100644 (file)
@@ -108,7 +108,7 @@ static void egpio_handler(unsigned int irq, struct irq_desc *desc)
        ack_irqs(ei);
        /* Process all set pins. */
        readval &= ei->irqs_enabled;
-       for_each_bit(irqpin, &readval, ei->nirqs) {
+       for_each_set_bit(irqpin, &readval, ei->nirqs) {
                /* Run irq handler */
                pr_debug("got IRQ %d\n", irqpin);
                irq = ei->irq_start + irqpin;
index 735c8a4d164f0d503bcb5ddc5c2323dc3b6eae0d..62a847e4c2d840f8f6c28c06785a0c49a1604c57 100644 (file)
@@ -225,7 +225,7 @@ int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset,
 }
 EXPORT_SYMBOL(mc13783_reg_rmw);
 
-int mc13783_mask(struct mc13783 *mc13783, int irq)
+int mc13783_irq_mask(struct mc13783 *mc13783, int irq)
 {
        int ret;
        unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
@@ -245,9 +245,9 @@ int mc13783_mask(struct mc13783 *mc13783, int irq)
 
        return mc13783_reg_write(mc13783, offmask, mask | irqbit);
 }
-EXPORT_SYMBOL(mc13783_mask);
+EXPORT_SYMBOL(mc13783_irq_mask);
 
-int mc13783_unmask(struct mc13783 *mc13783, int irq)
+int mc13783_irq_unmask(struct mc13783 *mc13783, int irq)
 {
        int ret;
        unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
@@ -267,7 +267,53 @@ int mc13783_unmask(struct mc13783 *mc13783, int irq)
 
        return mc13783_reg_write(mc13783, offmask, mask & ~irqbit);
 }
-EXPORT_SYMBOL(mc13783_unmask);
+EXPORT_SYMBOL(mc13783_irq_unmask);
+
+int mc13783_irq_status(struct mc13783 *mc13783, int irq,
+               int *enabled, int *pending)
+{
+       int ret;
+       unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1;
+       unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
+       u32 irqbit = 1 << (irq < 24 ? irq : irq - 24);
+
+       if (irq < 0 || irq >= MC13783_NUM_IRQ)
+               return -EINVAL;
+
+       if (enabled) {
+               u32 mask;
+
+               ret = mc13783_reg_read(mc13783, offmask, &mask);
+               if (ret)
+                       return ret;
+
+               *enabled = mask & irqbit;
+       }
+
+       if (pending) {
+               u32 stat;
+
+               ret = mc13783_reg_read(mc13783, offstat, &stat);
+               if (ret)
+                       return ret;
+
+               *pending = stat & irqbit;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(mc13783_irq_status);
+
+int mc13783_irq_ack(struct mc13783 *mc13783, int irq)
+{
+       unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
+       unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
+
+       BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
+
+       return mc13783_reg_write(mc13783, offstat, val);
+}
+EXPORT_SYMBOL(mc13783_irq_ack);
 
 int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
                irq_handler_t handler, const char *name, void *dev)
@@ -297,7 +343,7 @@ int mc13783_irq_request(struct mc13783 *mc13783, int irq,
        if (ret)
                return ret;
 
-       ret = mc13783_unmask(mc13783, irq);
+       ret = mc13783_irq_unmask(mc13783, irq);
        if (ret) {
                mc13783->irqhandler[irq] = NULL;
                mc13783->irqdata[irq] = NULL;
@@ -317,7 +363,7 @@ int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev)
                        mc13783->irqdata[irq] != dev)
                return -EINVAL;
 
-       ret = mc13783_mask(mc13783, irq);
+       ret = mc13783_irq_mask(mc13783, irq);
        if (ret)
                return ret;
 
@@ -333,17 +379,6 @@ static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq)
        return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]);
 }
 
-int mc13783_ackirq(struct mc13783 *mc13783, int irq)
-{
-       unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1;
-       unsigned int val = 1 << (irq < 24 ? irq : irq - 24);
-
-       BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ);
-
-       return mc13783_reg_write(mc13783, offstat, val);
-}
-EXPORT_SYMBOL(mc13783_ackirq);
-
 /*
  * returns: number of handled irqs or negative error
  * locking: holds mc13783->lock
@@ -422,7 +457,7 @@ static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
 {
        struct mc13783_adcdone_data *adcdone_data = data;
 
-       mc13783_ackirq(adcdone_data->mc13783, irq);
+       mc13783_irq_ack(adcdone_data->mc13783, irq);
 
        complete_all(&adcdone_data->done);
 
@@ -486,7 +521,7 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
        dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__);
        mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE,
                        mc13783_handler_adcdone, __func__, &adcdone_data);
-       mc13783_ackirq(mc13783, MC13783_IRQ_ADCDONE);
+       mc13783_irq_ack(mc13783, MC13783_IRQ_ADCDONE);
 
        mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0);
        mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1);
index e3551d20464fe8f56c9edecfd9babe8c496e1aec..d16af6a423fbc7d409c56f531531788733419eac 100644 (file)
@@ -212,6 +212,15 @@ config CS5535_MFGPT_DEFAULT_IRQ
          want to use a different IRQ by default.  This is here for
          architectures to set as necessary.
 
+config CS5535_CLOCK_EVENT_SRC
+       tristate "CS5535/CS5536 high-res timer (MFGPT) events"
+       depends on GENERIC_TIME && GENERIC_CLOCKEVENTS && CS5535_MFGPT
+       help
+         This driver provides a clock event source based on the MFGPT
+         timer(s) in the CS5535 and CS5536 companion chips.
+         MFGPTs have a better resolution and max interval than the
+         generic PIT, and are suitable for use as high-res timers.
+
 config HP_ILO
        tristate "Channel interface driver for HP iLO/iLO2 processor"
        depends on PCI
index dd0a3913bf6d00cd14dfec797fa89026b4218d87..3b7292a5cea95916cdf4574393c6664a38bc648b 100644 (file)
@@ -597,8 +597,6 @@ static void iwmct_remove(struct sdio_func *func)
        struct iwmct_work_struct *read_req;
        struct iwmct_priv *priv = sdio_get_drvdata(func);
 
-       priv = sdio_get_drvdata(func);
-
        LOG_INFO(priv, INIT, "enter\n");
 
        sdio_claim_host(func);
index 3648b23d5c92bea5a7697b5a89e81f2cff85c9e6..4a0648301fdfc4af0762c76e891ad3cfa0c3de9d 100644 (file)
  * It is adapted from the Linux Kernel Dump Test Tool by
  * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net>
  *
- * Usage :  insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<>
- *                                                     [cpoint_count={>0}]
+ * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net>
  *
- * recur_count : Recursion level for the stack overflow test. Default is 10.
- *
- * cpoint_name : Crash point where the kernel is to be crashed. It can be
- *              one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY,
- *              FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD,
- *              IDE_CORE_CP
- *
- * cpoint_type : Indicates the action to be taken on hitting the crash point.
- *              It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW
- *
- * cpoint_count : Indicates the number of times the crash point is to be hit
- *               to trigger an action. The default is 10.
+ * See Documentation/fault-injection/provoke-crashes.txt for instructions
  */
 
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/hrtimer.h>
 #include <scsi/scsi_cmnd.h>
+#include <linux/debugfs.h>
 
 #ifdef CONFIG_IDE
 #include <linux/ide.h>
 #endif
 
-#define NUM_CPOINTS 8
-#define NUM_CPOINT_TYPES 5
 #define DEFAULT_COUNT 10
 #define REC_NUM_DEFAULT 10
 
@@ -72,7 +59,8 @@ enum cname {
        MEM_SWAPOUT,
        TIMERADD,
        SCSI_DISPATCH_CMD,
-       IDE_CORE_CP
+       IDE_CORE_CP,
+       DIRECT,
 };
 
 enum ctype {
@@ -81,7 +69,11 @@ enum ctype {
        BUG,
        EXCEPTION,
        LOOP,
-       OVERFLOW
+       OVERFLOW,
+       CORRUPT_STACK,
+       UNALIGNED_LOAD_STORE_WRITE,
+       OVERWRITE_ALLOCATION,
+       WRITE_AFTER_FREE,
 };
 
 static char* cp_name[] = {
@@ -92,7 +84,8 @@ static char* cp_name[] = {
        "MEM_SWAPOUT",
        "TIMERADD",
        "SCSI_DISPATCH_CMD",
-       "IDE_CORE_CP"
+       "IDE_CORE_CP",
+       "DIRECT",
 };
 
 static char* cp_type[] = {
@@ -100,7 +93,11 @@ static char* cp_type[] = {
        "BUG",
        "EXCEPTION",
        "LOOP",
-       "OVERFLOW"
+       "OVERFLOW",
+       "CORRUPT_STACK",
+       "UNALIGNED_LOAD_STORE_WRITE",
+       "OVERWRITE_ALLOCATION",
+       "WRITE_AFTER_FREE",
 };
 
 static struct jprobe lkdtm;
@@ -193,34 +190,66 @@ int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
 }
 #endif
 
+/* Return the crashpoint number or NONE if the name is invalid */
+static enum ctype parse_cp_type(const char *what, size_t count)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cp_type); i++) {
+               if (!strcmp(what, cp_type[i]))
+                       return i + 1;
+       }
+
+       return NONE;
+}
+
+static const char *cp_type_to_str(enum ctype type)
+{
+       if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type))
+               return "None";
+
+       return cp_type[type - 1];
+}
+
+static const char *cp_name_to_str(enum cname name)
+{
+       if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
+               return "INVALID";
+
+       return cp_name[name - 1];
+}
+
+
 static int lkdtm_parse_commandline(void)
 {
        int i;
 
-       if (cpoint_name == NULL || cpoint_type == NULL ||
-                                       cpoint_count < 1 || recur_count < 1)
+       if (cpoint_count < 1 || recur_count < 1)
                return -EINVAL;
 
-       for (i = 0; i < NUM_CPOINTS; ++i) {
+       count = cpoint_count;
+
+       /* No special parameters */
+       if (!cpoint_type && !cpoint_name)
+               return 0;
+
+       /* Neither or both of these need to be set */
+       if (!cpoint_type || !cpoint_name)
+               return -EINVAL;
+
+       cptype = parse_cp_type(cpoint_type, strlen(cpoint_type));
+       if (cptype == NONE)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
                if (!strcmp(cpoint_name, cp_name[i])) {
                        cpoint = i + 1;
-                       break;
-               }
-       }
-
-       for (i = 0; i < NUM_CPOINT_TYPES; ++i) {
-               if (!strcmp(cpoint_type, cp_type[i])) {
-                       cptype = i + 1;
-                       break;
+                       return 0;
                }
        }
 
-       if (cpoint == INVALID || cptype == NONE)
-                return -EINVAL;
-
-       count = cpoint_count;
-
-       return 0;
+       /* Could not find a valid crash point */
+       return -EINVAL;
 }
 
 static int recursive_loop(int a)
@@ -235,53 +264,92 @@ static int recursive_loop(int a)
                return recursive_loop(a);
 }
 
-void lkdtm_handler(void)
+static void lkdtm_do_action(enum ctype which)
 {
-       printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n",
-                                        cpoint_name, cpoint_type);
-       --count;
+       switch (which) {
+       case PANIC:
+               panic("dumptest");
+               break;
+       case BUG:
+               BUG();
+               break;
+       case EXCEPTION:
+               *((int *) 0) = 0;
+               break;
+       case LOOP:
+               for (;;)
+                       ;
+               break;
+       case OVERFLOW:
+               (void) recursive_loop(0);
+               break;
+       case CORRUPT_STACK: {
+               volatile u32 data[8];
+               volatile u32 *p = data;
+
+               p[12] = 0x12345678;
+               break;
+       }
+       case UNALIGNED_LOAD_STORE_WRITE: {
+               static u8 data[5] __attribute__((aligned(4))) = {1, 2,
+                               3, 4, 5};
+               u32 *p;
+               u32 val = 0x12345678;
+
+               p = (u32 *)(data + 1);
+               if (*p == 0)
+                       val = 0x87654321;
+               *p = val;
+                break;
+       }
+       case OVERWRITE_ALLOCATION: {
+               size_t len = 1020;
+               u32 *data = kmalloc(len, GFP_KERNEL);
+
+               data[1024 / sizeof(u32)] = 0x12345678;
+               kfree(data);
+               break;
+       }
+       case WRITE_AFTER_FREE: {
+               size_t len = 1024;
+               u32 *data = kmalloc(len, GFP_KERNEL);
+
+               kfree(data);
+               schedule();
+               memset(data, 0x78, len);
+               break;
+       }
+       case NONE:
+       default:
+               break;
+       }
+
+}
+
+static void lkdtm_handler(void)
+{
+       count--;
+       printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n",
+                       cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
 
        if (count == 0) {
-               switch (cptype) {
-               case NONE:
-                       break;
-               case PANIC:
-                       printk(KERN_INFO "lkdtm : PANIC\n");
-                       panic("dumptest");
-                       break;
-               case BUG:
-                       printk(KERN_INFO "lkdtm : BUG\n");
-                       BUG();
-                       break;
-               case EXCEPTION:
-                       printk(KERN_INFO "lkdtm : EXCEPTION\n");
-                       *((int *) 0) = 0;
-                       break;
-               case LOOP:
-                       printk(KERN_INFO "lkdtm : LOOP\n");
-                       for (;;);
-                       break;
-               case OVERFLOW:
-                       printk(KERN_INFO "lkdtm : OVERFLOW\n");
-                       (void) recursive_loop(0);
-                       break;
-               default:
-                       break;
-               }
+               lkdtm_do_action(cptype);
                count = cpoint_count;
        }
 }
 
-static int __init lkdtm_module_init(void)
+static int lkdtm_register_cpoint(enum cname which)
 {
        int ret;
 
-       if (lkdtm_parse_commandline() == -EINVAL) {
-               printk(KERN_INFO "lkdtm : Invalid command\n");
-               return -EINVAL;
-       }
+       cpoint = INVALID;
+       if (lkdtm.entry != NULL)
+               unregister_jprobe(&lkdtm);
 
-       switch (cpoint) {
+       switch (which) {
+       case DIRECT:
+               lkdtm_do_action(cptype);
+               return 0;
        case INT_HARDWARE_ENTRY:
                lkdtm.kp.symbol_name = "do_IRQ";
                lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
@@ -315,28 +383,268 @@ static int __init lkdtm_module_init(void)
                lkdtm.kp.symbol_name = "generic_ide_ioctl";
                lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
 #else
-               printk(KERN_INFO "lkdtm : Crash point not available\n");
+               printk(KERN_INFO "lkdtm: Crash point not available\n");
+               return -EINVAL;
 #endif
                break;
        default:
-               printk(KERN_INFO "lkdtm : Invalid Crash Point\n");
-               break;
+               printk(KERN_INFO "lkdtm: Invalid Crash Point\n");
+               return -EINVAL;
        }
 
+       cpoint = which;
        if ((ret = register_jprobe(&lkdtm)) < 0) {
-                printk(KERN_INFO "lkdtm : Couldn't register jprobe\n");
-                return ret;
+               printk(KERN_INFO "lkdtm: Couldn't register jprobe\n");
+               cpoint = INVALID;
+       }
+
+       return ret;
+}
+
+static ssize_t do_register_entry(enum cname which, struct file *f,
+               const char __user *user_buf, size_t count, loff_t *off)
+{
+       char *buf;
+       int err;
+
+       if (count >= PAGE_SIZE)
+               return -EINVAL;
+
+       buf = (char *)__get_free_page(GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       if (copy_from_user(buf, user_buf, count)) {
+               free_page((unsigned long) buf);
+               return -EFAULT;
+       }
+       /* NULL-terminate and remove enter */
+       buf[count] = '\0';
+       strim(buf);
+
+       cptype = parse_cp_type(buf, count);
+       free_page((unsigned long) buf);
+
+       if (cptype == NONE)
+               return -EINVAL;
+
+       err = lkdtm_register_cpoint(which);
+       if (err < 0)
+               return err;
+
+       *off += count;
+
+       return count;
+}
+
+/* Generic read callback that just prints out the available crash types */
+static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
+               size_t count, loff_t *off)
+{
+       char *buf;
+       int i, n, out;
+
+       buf = (char *)__get_free_page(GFP_KERNEL);
+
+       n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
+       for (i = 0; i < ARRAY_SIZE(cp_type); i++)
+               n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]);
+       buf[n] = '\0';
+
+       out = simple_read_from_buffer(user_buf, count, off,
+                                     buf, n);
+       free_page((unsigned long) buf);
+
+       return out;
+}
+
+static int lkdtm_debugfs_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+
+static ssize_t int_hardware_entry(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off);
+}
+
+static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off);
+}
+
+static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off);
+}
+
+static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(FS_DEVRW, f, buf, count, off);
+}
+
+static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(MEM_SWAPOUT, f, buf, count, off);
+}
+
+static ssize_t timeradd_entry(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(TIMERADD, f, buf, count, off);
+}
+
+static ssize_t scsi_dispatch_cmd_entry(struct file *f,
+               const char __user *buf, size_t count, loff_t *off)
+{
+       return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off);
+}
+
+static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
+               size_t count, loff_t *off)
+{
+       return do_register_entry(IDE_CORE_CP, f, buf, count, off);
+}
+
+/* Special entry to just crash directly. Available without KPROBEs */
+static ssize_t direct_entry(struct file *f, const char __user *user_buf,
+               size_t count, loff_t *off)
+{
+       enum ctype type;
+       char *buf;
+
+       if (count >= PAGE_SIZE)
+               return -EINVAL;
+       if (count < 1)
+               return -EINVAL;
+
+       buf = (char *)__get_free_page(GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       if (copy_from_user(buf, user_buf, count)) {
+               free_page((unsigned long) buf);
+               return -EFAULT;
+       }
+       /* NULL-terminate and remove enter */
+       buf[count] = '\0';
+       strim(buf);
+
+       type = parse_cp_type(buf, count);
+       free_page((unsigned long) buf);
+       if (type == NONE)
+               return -EINVAL;
+
+       printk(KERN_INFO "lkdtm: Performing direct entry %s\n",
+                       cp_type_to_str(type));
+       lkdtm_do_action(type);
+       *off += count;
+
+       return count;
+}
+
+struct crash_entry {
+       const char *name;
+       const struct file_operations fops;
+};
+
+static const struct crash_entry crash_entries[] = {
+       {"DIRECT", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = direct_entry} },
+       {"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = int_hardware_entry} },
+       {"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = int_hw_irq_en} },
+       {"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = int_tasklet_entry} },
+       {"FS_DEVRW", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = fs_devrw_entry} },
+       {"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = mem_swapout_entry} },
+       {"TIMERADD", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = timeradd_entry} },
+       {"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = scsi_dispatch_cmd_entry} },
+       {"IDE_CORE_CP", {.read = lkdtm_debugfs_read,
+                       .open = lkdtm_debugfs_open,
+                       .write = ide_core_cp_entry} },
+};
+
+static struct dentry *lkdtm_debugfs_root;
+
+static int __init lkdtm_module_init(void)
+{
+       int ret = -EINVAL;
+       int n_debugfs_entries = 1; /* Assume only the direct entry */
+       int i;
+
+       /* Register debugfs interface */
+       lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
+       if (!lkdtm_debugfs_root) {
+               printk(KERN_ERR "lkdtm: creating root dir failed\n");
+               return -ENODEV;
+       }
+
+#ifdef CONFIG_KPROBES
+       n_debugfs_entries = ARRAY_SIZE(crash_entries);
+#endif
+
+       for (i = 0; i < n_debugfs_entries; i++) {
+               const struct crash_entry *cur = &crash_entries[i];
+               struct dentry *de;
+
+               de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
+                               NULL, &cur->fops);
+               if (de == NULL) {
+                       printk(KERN_ERR "lkdtm: could not create %s\n",
+                                       cur->name);
+                       goto out_err;
+               }
+       }
+
+       if (lkdtm_parse_commandline() == -EINVAL) {
+               printk(KERN_INFO "lkdtm: Invalid command\n");
+               goto out_err;
+       }
+
+       if (cpoint != INVALID && cptype != NONE) {
+               ret = lkdtm_register_cpoint(cpoint);
+               if (ret < 0) {
+                       printk(KERN_INFO "lkdtm: Invalid crash point %d\n",
+                                       cpoint);
+                       goto out_err;
+               }
+               printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n",
+                               cpoint_name, cpoint_type);
+       } else {
+               printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n");
        }
 
-       printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n",
-                                               cpoint_name, cpoint_type);
        return 0;
+
+out_err:
+       debugfs_remove_recursive(lkdtm_debugfs_root);
+       return ret;
 }
 
 static void __exit lkdtm_module_exit(void)
 {
-        unregister_jprobe(&lkdtm);
-        printk(KERN_INFO "lkdtm : Crash point unregistered\n");
+       debugfs_remove_recursive(lkdtm_debugfs_root);
+
+       unregister_jprobe(&lkdtm);
+       printk(KERN_INFO "lkdtm: Crash point unregistered\n");
 }
 
 module_init(lkdtm_module_init);
index 16f0abda1423bf43e0604a5484e261d52651efa6..57b152f8d1b9c8dc1a90a09af32f8bdb8bfd874d 100644 (file)
@@ -475,7 +475,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        if (skb->data[0] == 0xff) {
                /* we are being asked to broadcast to all partitions */
-               for_each_bit(dest_partid, xpnet_broadcast_partitions,
+               for_each_set_bit(dest_partid, xpnet_broadcast_partitions,
                             xp_max_npartitions) {
 
                        xpnet_send(skb, queued_msg, start_addr, end_addr,
index 30acd526582162033ec3d35d4f770bfa876a41fd..f4b97d3c3d0fcaf00fb8feac736da4e1c98cb6c6 100644 (file)
@@ -1151,6 +1151,9 @@ void mmc_stop_host(struct mmc_host *host)
        cancel_delayed_work(&host->detect);
        mmc_flush_scheduled_work();
 
+       /* clear pm flags now and let card drivers set them as needed */
+       host->pm_flags = 0;
+
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
                if (host->bus_ops->remove)
@@ -1273,12 +1276,13 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
                        mmc_claim_host(host);
                        mmc_detach_bus(host);
                        mmc_release_host(host);
+                       host->pm_flags = 0;
                        err = 0;
                }
        }
        mmc_bus_put(host);
 
-       if (!err)
+       if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER))
                mmc_power_off(host);
 
        return err;
@@ -1296,8 +1300,10 @@ int mmc_resume_host(struct mmc_host *host)
 
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
-               mmc_power_up(host);
-               mmc_select_voltage(host, host->ocr);
+               if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
+                       mmc_power_up(host);
+                       mmc_select_voltage(host, host->ocr);
+               }
                BUG_ON(!host->bus_ops->resume);
                err = host->bus_ops->resume(host);
                if (err) {
index 06b64085a355b3aed5198548b406ddcc79174a48..2dd4cfe7ca17d8da17ef24fe6f89932e2771e755 100644 (file)
@@ -187,6 +187,40 @@ static int sdio_disable_cd(struct mmc_card *card)
        return mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
 }
 
+/*
+ * Devices that remain active during a system suspend are
+ * put back into 1-bit mode.
+ */
+static int sdio_disable_wide(struct mmc_card *card)
+{
+       int ret;
+       u8 ctrl;
+
+       if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+               return 0;
+
+       if (card->cccr.low_speed && !card->cccr.wide_bus)
+               return 0;
+
+       ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
+       if (ret)
+               return ret;
+
+       if (!(ctrl & SDIO_BUS_WIDTH_4BIT))
+               return 0;
+
+       ctrl &= ~SDIO_BUS_WIDTH_4BIT;
+       ctrl |= SDIO_BUS_ASYNC_INT;
+
+       ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
+       if (ret)
+               return ret;
+
+       mmc_set_bus_width(card->host, MMC_BUS_WIDTH_1);
+
+       return 0;
+}
+
 /*
  * Test if the card supports high-speed mode and, if so, switch to it.
  */
@@ -224,7 +258,7 @@ static int sdio_enable_hs(struct mmc_card *card)
  * we're trying to reinitialise.
  */
 static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
-                             struct mmc_card *oldcard)
+                             struct mmc_card *oldcard, int powered_resume)
 {
        struct mmc_card *card;
        int err;
@@ -235,9 +269,11 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
        /*
         * Inform the card of the voltage
         */
-       err = mmc_send_io_op_cond(host, host->ocr, &ocr);
-       if (err)
-               goto err;
+       if (!powered_resume) {
+               err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+               if (err)
+                       goto err;
+       }
 
        /*
         * For SPI, enable CRC as appropriate.
@@ -262,7 +298,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
        /*
         * For native busses:  set card RCA and quit open drain mode.
         */
-       if (!mmc_host_is_spi(host)) {
+       if (!powered_resume && !mmc_host_is_spi(host)) {
                err = mmc_send_relative_addr(host, &card->rca);
                if (err)
                        goto remove;
@@ -273,7 +309,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
        /*
         * Select card, as all following commands rely on that.
         */
-       if (!mmc_host_is_spi(host)) {
+       if (!powered_resume && !mmc_host_is_spi(host)) {
                err = mmc_select_card(card);
                if (err)
                        goto remove;
@@ -425,6 +461,12 @@ static int mmc_sdio_suspend(struct mmc_host *host)
                }
        }
 
+       if (!err && host->pm_flags & MMC_PM_KEEP_POWER) {
+               mmc_claim_host(host);
+               sdio_disable_wide(host->card);
+               mmc_release_host(host);
+       }
+
        return err;
 }
 
@@ -437,7 +479,13 @@ static int mmc_sdio_resume(struct mmc_host *host)
 
        /* Basic card reinitialization. */
        mmc_claim_host(host);
-       err = mmc_sdio_init_card(host, host->ocr, host->card);
+       err = mmc_sdio_init_card(host, host->ocr, host->card,
+                                (host->pm_flags & MMC_PM_KEEP_POWER));
+       if (!err)
+               /* We may have switched to 1-bit mode during suspend. */
+               err = sdio_enable_wide(host->card);
+       if (!err && host->sdio_irqs)
+               mmc_signal_sdio_irq(host);
        mmc_release_host(host);
 
        /*
@@ -507,7 +555,7 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
        /*
         * Detect and init the card.
         */
-       err = mmc_sdio_init_card(host, host->ocr, NULL);
+       err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
        if (err)
                goto err;
        card = host->card;
index f9aa8a7deffaff1788955284e3f39dc86f8725a2..ff27c8c713554024dd0b2530cda395dd2d296b94 100644 (file)
@@ -189,7 +189,12 @@ static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
 {
        unsigned mval = min(func->card->host->max_seg_size,
                            func->card->host->max_blk_size);
-       mval = min(mval, func->max_blksize);
+
+       if (mmc_blksz_for_byte_mode(func->card))
+               mval = min(mval, func->cur_blksize);
+       else
+               mval = min(mval, func->max_blksize);
+
        return min(mval, 512u); /* maximum size for byte mode */
 }
 
@@ -635,3 +640,52 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
                *err_ret = ret;
 }
 EXPORT_SYMBOL_GPL(sdio_f0_writeb);
+
+/**
+ *     sdio_get_host_pm_caps - get host power management capabilities
+ *     @func: SDIO function attached to host
+ *
+ *     Returns a capability bitmask corresponding to power management
+ *     features supported by the host controller that the card function
+ *     might rely upon during a system suspend.  The host doesn't need
+ *     to be claimed, nor the function active, for this information to be
+ *     obtained.
+ */
+mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func)
+{
+       BUG_ON(!func);
+       BUG_ON(!func->card);
+
+       return func->card->host->pm_caps;
+}
+EXPORT_SYMBOL_GPL(sdio_get_host_pm_caps);
+
+/**
+ *     sdio_set_host_pm_flags - set wanted host power management capabilities
+ *     @func: SDIO function attached to host
+ *
+ *     Set a capability bitmask corresponding to wanted host controller
+ *     power management features for the upcoming suspend state.
+ *     This must be called, if needed, each time the suspend method of
+ *     the function driver is called, and must contain only bits that
+ *     were returned by sdio_get_host_pm_caps().
+ *     The host doesn't need to be claimed, nor the function active,
+ *     for this information to be set.
+ */
+int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags)
+{
+       struct mmc_host *host;
+
+       BUG_ON(!func);
+       BUG_ON(!func->card);
+
+       host = func->card->host;
+
+       if (flags & ~host->pm_caps)
+               return -EINVAL;
+
+       /* function suspend methods are serialized, hence no lock needed */
+       host->pm_flags |= flags;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags);
index ce1d28884e2987a059f9cba3ead143d041fc9a57..7b431bbab7f13f300e64b66c6f1383c6a0e0df00 100644 (file)
@@ -69,20 +69,16 @@ config MMC_SDHCI_PCI
          If unsure, say N.
 
 config MMC_RICOH_MMC
-       tristate "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"
+       bool "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"
        depends on MMC_SDHCI_PCI
        help
-         This selects the disabler for the Ricoh MMC Controller. This
+         This adds a pci quirk to disable Ricoh MMC Controller. This
          proprietary controller is unnecessary because the SDHCI driver
          supports MMC cards on the SD controller, but if it is not
          disabled, it will steal the MMC cards away - rendering them
-         useless. It is safe to select this driver even if you don't
+         useless. It is safe to select this even if you don't
          have a Ricoh based card reader.
 
-
-         To compile this driver as a module, choose M here:
-         the module will be called ricoh_mmc.
-
          If unsure, say Y.
 
 config MMC_SDHCI_OF
@@ -193,6 +189,7 @@ config MMC_AU1X
 
 choice
        prompt "Atmel SD/MMC Driver"
+       depends on AVR32 || ARCH_AT91
        default MMC_ATMELMCI if AVR32
        help
          Choose which driver to use for the Atmel MCI Silicon
@@ -399,7 +396,7 @@ config MMC_VIA_SDMMC
 
 config SDH_BFIN
        tristate "Blackfin Secure Digital Host support"
-       depends on MMC && ((BF54x && !BF544) || (BF51x && !BF512))
+       depends on (BF54x && !BF544) || (BF51x && !BF512)
        help
          If you say yes here you will get support for the Blackfin on-chip
          Secure Digital Host interface.  This includes support for MMC and
index 3d253dd4240fee2c7d8fbb42f631563af370fb36..f4803977dfceb2b8f71d4514e0af8c115c923aee 100644 (file)
@@ -12,7 +12,6 @@ obj-$(CONFIG_MMC_IMX)         += imxmmc.o
 obj-$(CONFIG_MMC_MXC)          += mxcmmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
-obj-$(CONFIG_MMC_RICOH_MMC)    += ricoh_mmc.o
 obj-$(CONFIG_MMC_SDHCI_PLTFM)  += sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_S3C)    += sdhci-s3c.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
index 63924e0c7ea9a6c3fab8c2b38857be8832149612..91dc60cd032b37b676226f508d87329b6af065ae 100644 (file)
 
 #define DRIVER_NAME "at91_mci"
 
+static inline int at91mci_is_mci1rev2xx(void)
+{
+       return (   cpu_is_at91sam9260()
+               || cpu_is_at91sam9263()
+               || cpu_is_at91cap9()
+               || cpu_is_at91sam9rl()
+               || cpu_is_at91sam9g10()
+               || cpu_is_at91sam9g20()
+               );
+}
+
 #define FL_SENT_COMMAND        (1 << 0)
 #define FL_SENT_STOP   (1 << 1)
 
 #define at91_mci_read(host, reg)       __raw_readl((host)->baseaddr + (reg))
 #define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg))
 
+#define MCI_BLKSIZE            512
+#define MCI_MAXBLKSIZE                 4095
+#define MCI_BLKATONCE          256
+#define MCI_BUFSIZE            (MCI_BLKSIZE * MCI_BLKATONCE)
 
 /*
  * Low level type for this driver
@@ -200,8 +215,8 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
        size = data->blksz * data->blocks;
        len = data->sg_len;
 
-       /* AT91SAM926[0/3] Data Write Operation and number of bytes erratum */
-       if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+       /* MCI1 rev2xx Data Write Operation and number of bytes erratum */
+       if (at91mci_is_mci1rev2xx())
                if (host->total_length == 12)
                        memset(dmabuf, 0, 12);
 
@@ -227,8 +242,10 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
                        for (index = 0; index < (amount / 4); index++)
                                *dmabuf++ = swab32(sgbuffer[index]);
                } else {
-                       memcpy(dmabuf, sgbuffer, amount);
-                       dmabuf += amount;
+                       char *tmpv = (char *)dmabuf;
+                       memcpy(tmpv, sgbuffer, amount);
+                       tmpv += amount;
+                       dmabuf = (unsigned *)tmpv;
                }
 
                kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
@@ -244,74 +261,6 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
        BUG_ON(size != 0);
 }
 
-/*
- * Prepare a dma read
- */
-static void at91_mci_pre_dma_read(struct at91mci_host *host)
-{
-       int i;
-       struct scatterlist *sg;
-       struct mmc_command *cmd;
-       struct mmc_data *data;
-
-       pr_debug("pre dma read\n");
-
-       cmd = host->cmd;
-       if (!cmd) {
-               pr_debug("no command\n");
-               return;
-       }
-
-       data = cmd->data;
-       if (!data) {
-               pr_debug("no data\n");
-               return;
-       }
-
-       for (i = 0; i < 2; i++) {
-               /* nothing left to transfer */
-               if (host->transfer_index >= data->sg_len) {
-                       pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index);
-                       break;
-               }
-
-               /* Check to see if this needs filling */
-               if (i == 0) {
-                       if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) {
-                               pr_debug("Transfer active in current\n");
-                               continue;
-                       }
-               }
-               else {
-                       if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) {
-                               pr_debug("Transfer active in next\n");
-                               continue;
-                       }
-               }
-
-               /* Setup the next transfer */
-               pr_debug("Using transfer index %d\n", host->transfer_index);
-
-               sg = &data->sg[host->transfer_index++];
-               pr_debug("sg = %p\n", sg);
-
-               sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE);
-
-               pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length);
-
-               if (i == 0) {
-                       at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
-                       at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
-               }
-               else {
-                       at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
-                       at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
-               }
-       }
-
-       pr_debug("pre dma read done\n");
-}
-
 /*
  * Handle after a dma read
  */
@@ -319,6 +268,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
 {
        struct mmc_command *cmd;
        struct mmc_data *data;
+       unsigned int len, i, size;
+       unsigned *dmabuf = host->buffer;
 
        pr_debug("post dma read\n");
 
@@ -334,42 +285,39 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
                return;
        }
 
-       while (host->in_use_index < host->transfer_index) {
-               struct scatterlist *sg;
+       size = data->blksz * data->blocks;
+       len = data->sg_len;
 
-               pr_debug("finishing index %d\n", host->in_use_index);
+       at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
+       at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
 
-               sg = &data->sg[host->in_use_index++];
+       for (i = 0; i < len; i++) {
+               struct scatterlist *sg;
+               int amount;
+               unsigned int *sgbuffer;
 
-               pr_debug("Unmapping page %08X\n", sg->dma_address);
+               sg = &data->sg[i];
 
-               dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
+               sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+               amount = min(size, sg->length);
+               size -= amount;
 
                if (cpu_is_at91rm9200()) {      /* AT91RM9200 errata */
-                       unsigned int *buffer;
                        int index;
-
-                       /* Swap the contents of the buffer */
-                       buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
-                       pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
-
-                       for (index = 0; index < (sg->length / 4); index++)
-                               buffer[index] = swab32(buffer[index]);
-
-                       kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+                       for (index = 0; index < (amount / 4); index++)
+                               sgbuffer[index] = swab32(*dmabuf++);
+               } else {
+                       char *tmpv = (char *)dmabuf;
+                       memcpy(sgbuffer, tmpv, amount);
+                       tmpv += amount;
+                       dmabuf = (unsigned *)tmpv;
                }
 
-               flush_dcache_page(sg_page(sg));
-
-               data->bytes_xfered += sg->length;
-       }
-
-       /* Is there another transfer to trigger? */
-       if (host->transfer_index < data->sg_len)
-               at91_mci_pre_dma_read(host);
-       else {
-               at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
-               at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
+               kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
+               dmac_flush_range((void *)sgbuffer, ((void *)sgbuffer) + amount);
+               data->bytes_xfered += amount;
+               if (size == 0)
+                       break;
        }
 
        pr_debug("post dma read done\n");
@@ -461,7 +409,7 @@ static void at91_mci_enable(struct at91mci_host *host)
        at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
        mr = AT91_MCI_PDCMODE | 0x34a;
 
-       if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+       if (at91mci_is_mci1rev2xx())
                mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
 
        at91_mci_write(host, AT91_MCI_MR, mr);
@@ -602,10 +550,14 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
                                /*
                                 * Handle a read
                                 */
-                               host->buffer = NULL;
                                host->total_length = 0;
 
-                               at91_mci_pre_dma_read(host);
+                               at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address);
+                               at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ?
+                                       (blocks * block_length) : (blocks * block_length) / 4);
+                               at91_mci_write(host, ATMEL_PDC_RNPR, 0);
+                               at91_mci_write(host, ATMEL_PDC_RNCR, 0);
+
                                ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
                        }
                        else {
@@ -614,27 +566,15 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
                                 */
                                host->total_length = block_length * blocks;
                                /*
-                                * AT91SAM926[0/3] Data Write Operation and
+                                * MCI1 rev2xx Data Write Operation and
                                 * number of bytes erratum
                                 */
-                               if (cpu_is_at91sam9260 () || cpu_is_at91sam9263())
+                               if (at91mci_is_mci1rev2xx())
                                        if (host->total_length < 12)
                                                host->total_length = 12;
 
-                               host->buffer = kmalloc(host->total_length, GFP_KERNEL);
-                               if (!host->buffer) {
-                                       pr_debug("Can't alloc tx buffer\n");
-                                       cmd->error = -ENOMEM;
-                                       mmc_request_done(host->mmc, host->request);
-                                       return;
-                               }
-
                                at91_mci_sg_to_dma(host, data);
 
-                               host->physical_address = dma_map_single(NULL,
-                                               host->buffer, host->total_length,
-                                               DMA_TO_DEVICE);
-
                                pr_debug("Transmitting %d bytes\n", host->total_length);
 
                                at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
@@ -701,14 +641,6 @@ static void at91_mci_completed_command(struct at91mci_host *host, unsigned int s
        cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
        cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
 
-       if (host->buffer) {
-               dma_unmap_single(NULL,
-                               host->physical_address, host->total_length,
-                               DMA_TO_DEVICE);
-               kfree(host->buffer);
-               host->buffer = NULL;
-       }
-
        pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n",
                 status, at91_mci_read(host, AT91_MCI_SR),
                 cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
@@ -754,7 +686,8 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        host->request = mrq;
        host->flags = 0;
 
-       mod_timer(&host->timer, jiffies +  HZ);
+       /* more than 1s timeout needed with slow SD cards */
+       mod_timer(&host->timer, jiffies +  msecs_to_jiffies(2000));
 
        at91_mci_process_next(host);
 }
@@ -942,7 +875,8 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
                        pr_debug("****** Resetting SD-card bus width ******\n");
                        at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
                }
-               mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+               /* 0.5s needed because of early card detect switch firing */
+               mmc_detect_change(host->mmc, msecs_to_jiffies(500));
        }
        return IRQ_HANDLED;
 }
@@ -1006,24 +940,42 @@ static int __init at91_mci_probe(struct platform_device *pdev)
        mmc->f_min = 375000;
        mmc->f_max = 25000000;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
-       mmc->caps = MMC_CAP_SDIO_IRQ;
+       mmc->caps = 0;
 
-       mmc->max_blk_size = 4095;
-       mmc->max_blk_count = mmc->max_req_size;
+       mmc->max_blk_size  = MCI_MAXBLKSIZE;
+       mmc->max_blk_count = MCI_BLKATONCE;
+       mmc->max_req_size  = MCI_BUFSIZE;
+       mmc->max_phys_segs = MCI_BLKATONCE;
+       mmc->max_hw_segs   = MCI_BLKATONCE;
+       mmc->max_seg_size  = MCI_BUFSIZE;
 
        host = mmc_priv(mmc);
        host->mmc = mmc;
-       host->buffer = NULL;
        host->bus_mode = 0;
        host->board = pdev->dev.platform_data;
        if (host->board->wire4) {
-               if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+               if (at91mci_is_mci1rev2xx())
                        mmc->caps |= MMC_CAP_4_BIT_DATA;
                else
                        dev_warn(&pdev->dev, "4 wire bus mode not supported"
                                " - using 1 wire\n");
        }
 
+       host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE,
+                                       &host->physical_address, GFP_KERNEL);
+       if (!host->buffer) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "Can't allocate transmit buffer\n");
+               goto fail5;
+       }
+
+       /* Add SDIO capability when available */
+       if (at91mci_is_mci1rev2xx()) {
+               /* at91mci MCI1 rev2xx sdio interrupt erratum */
+               if (host->board->wire4 || !host->board->slot_b)
+                       mmc->caps |= MMC_CAP_SDIO_IRQ;
+       }
+
        /*
         * Reserve GPIOs ... board init code makes sure these pins are set
         * up as GPIOs with the right direction (input, except for vcc)
@@ -1032,7 +984,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
                ret = gpio_request(host->board->det_pin, "mmc_detect");
                if (ret < 0) {
                        dev_dbg(&pdev->dev, "couldn't claim card detect pin\n");
-                       goto fail5;
+                       goto fail4b;
                }
        }
        if (host->board->wp_pin) {
@@ -1132,6 +1084,10 @@ fail3:
 fail4:
        if (host->board->det_pin)
                gpio_free(host->board->det_pin);
+fail4b:
+       if (host->buffer)
+               dma_free_coherent(&pdev->dev, MCI_BUFSIZE,
+                               host->buffer, host->physical_address);
 fail5:
        mmc_free_host(mmc);
 fail6:
@@ -1154,6 +1110,10 @@ static int __exit at91_mci_remove(struct platform_device *pdev)
 
        host = mmc_priv(mmc);
 
+       if (host->buffer)
+               dma_free_coherent(&pdev->dev, MCI_BUFSIZE,
+                               host->buffer, host->physical_address);
+
        if (host->board->det_pin) {
                if (device_can_wakeup(&pdev->dev))
                        free_irq(gpio_to_irq(host->board->det_pin), host);
index 3343a57355cc4a3a6e18e12f03f31badb6129913..56f7b448b9112e8360e4d31c2178431243d545bd 100644 (file)
@@ -115,7 +115,7 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
        unsigned int length;
        unsigned int data_ctl;
        unsigned int dma_cfg;
-       struct scatterlist *sg;
+       unsigned int cycle_ns, timeout;
 
        dev_dbg(mmc_dev(host->mmc), "%s enter flags: 0x%x\n", __func__, data->flags);
        host->data = data;
@@ -136,8 +136,11 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
        data_ctl |= ((ffs(data->blksz) - 1) << 4);
 
        bfin_write_SDH_DATA_CTL(data_ctl);
-
-       bfin_write_SDH_DATA_TIMER(0xFFFF);
+       /* the time of a host clock period in ns */
+       cycle_ns = 1000000000 / (get_sclk() / (2 * (host->clk_div + 1)));
+       timeout = data->timeout_ns / cycle_ns;
+       timeout += data->timeout_clks;
+       bfin_write_SDH_DATA_TIMER(timeout);
        SSYNC();
 
        if (data->flags & MMC_DATA_READ) {
@@ -151,6 +154,7 @@ static int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
 #if defined(CONFIG_BF54x)
        dma_cfg |= DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_32 | DMAEN;
        {
+               struct scatterlist *sg;
                int i;
                for_each_sg(data->sg, sg, host->dma_len, i) {
                        host->sg_cpu[i].start_addr = sg_dma_address(sg);
index dd45e7c3517eaff7107b420eaf39321b348bf0ba..3bd0ba294e9de667a56c2abf3915f77d43ad8b1b 100644 (file)
@@ -73,6 +73,7 @@
 /* DAVINCI_MMCCTL definitions */
 #define MMCCTL_DATRST         (1 << 0)
 #define MMCCTL_CMDRST         (1 << 1)
+#define MMCCTL_WIDTH_8_BIT    (1 << 8)
 #define MMCCTL_WIDTH_4_BIT    (1 << 2)
 #define MMCCTL_DATEG_DISABLED (0 << 6)
 #define MMCCTL_DATEG_RISING   (1 << 6)
@@ -791,22 +792,42 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios)
 
 static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
-       unsigned int mmc_pclk = 0;
        struct mmc_davinci_host *host = mmc_priv(mmc);
 
-       mmc_pclk = host->mmc_input_clk;
        dev_dbg(mmc_dev(host->mmc),
                "clock %dHz busmode %d powermode %d Vdd %04x\n",
                ios->clock, ios->bus_mode, ios->power_mode,
                ios->vdd);
-       if (ios->bus_width == MMC_BUS_WIDTH_4) {
-               dev_dbg(mmc_dev(host->mmc), "Enabling 4 bit mode\n");
-               writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_WIDTH_4_BIT,
-                       host->base + DAVINCI_MMCCTL);
-       } else {
-               dev_dbg(mmc_dev(host->mmc), "Disabling 4 bit mode\n");
-               writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_WIDTH_4_BIT,
+
+       switch (ios->bus_width) {
+       case MMC_BUS_WIDTH_8:
+               dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n");
+               writel((readl(host->base + DAVINCI_MMCCTL) &
+                       ~MMCCTL_WIDTH_4_BIT) | MMCCTL_WIDTH_8_BIT,
                        host->base + DAVINCI_MMCCTL);
+               break;
+       case MMC_BUS_WIDTH_4:
+               dev_dbg(mmc_dev(host->mmc), "Enabling 4 bit mode\n");
+               if (host->version == MMC_CTLR_VERSION_2)
+                       writel((readl(host->base + DAVINCI_MMCCTL) &
+                               ~MMCCTL_WIDTH_8_BIT) | MMCCTL_WIDTH_4_BIT,
+                               host->base + DAVINCI_MMCCTL);
+               else
+                       writel(readl(host->base + DAVINCI_MMCCTL) |
+                               MMCCTL_WIDTH_4_BIT,
+                               host->base + DAVINCI_MMCCTL);
+               break;
+       case MMC_BUS_WIDTH_1:
+               dev_dbg(mmc_dev(host->mmc), "Enabling 1 bit mode\n");
+               if (host->version == MMC_CTLR_VERSION_2)
+                       writel(readl(host->base + DAVINCI_MMCCTL) &
+                               ~(MMCCTL_WIDTH_8_BIT | MMCCTL_WIDTH_4_BIT),
+                               host->base + DAVINCI_MMCCTL);
+               else
+                       writel(readl(host->base + DAVINCI_MMCCTL) &
+                               ~MMCCTL_WIDTH_4_BIT,
+                               host->base + DAVINCI_MMCCTL);
+               break;
        }
 
        calculate_clk_divider(mmc, ios);
@@ -1189,10 +1210,14 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
 
        /* REVISIT:  someday, support IRQ-driven card detection.  */
        mmc->caps |= MMC_CAP_NEEDS_POLL;
+       mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
 
-       if (!pdata || pdata->wires == 4 || pdata->wires == 0)
+       if (pdata && (pdata->wires == 4 || pdata->wires == 0))
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
+       if (pdata && (pdata->wires == 8))
+               mmc->caps |= (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
+
        host->version = pdata->version;
 
        mmc->ops = &mmc_davinci_ops;
diff --git a/drivers/mmc/host/ricoh_mmc.c b/drivers/mmc/host/ricoh_mmc.c
deleted file mode 100644 (file)
index f627905..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- *  ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
- *
- *  Copyright (C) 2007 Philip Langdale, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- */
-
-/*
- * This is a conceptually ridiculous driver, but it is required by the way
- * the Ricoh multi-function chips (R5CXXX) work. These chips implement
- * the four main memory card controllers (SD, MMC, MS, xD) and one or both
- * of cardbus or firewire. It happens that they implement SD and MMC
- * support as separate controllers (and PCI functions). The linux SDHCI
- * driver supports MMC cards but the chip detects MMC cards in hardware
- * and directs them to the MMC controller - so the SDHCI driver never sees
- * them. To get around this, we must disable the useless MMC controller.
- * At that point, the SDHCI controller will start seeing them. As a bonus,
- * a detection event occurs immediately, even if the MMC card is already
- * in the reader.
- *
- * It seems to be the case that the relevant PCI registers to deactivate the
- * MMC controller live on PCI function 0, which might be the cardbus controller
- * or the firewire controller, depending on the particular chip in question. As
- * such, it makes what this driver has to do unavoidably ugly. Such is life.
- */
-
-#include <linux/pci.h>
-
-#define DRIVER_NAME "ricoh-mmc"
-
-static const struct pci_device_id pci_ids[] __devinitdata = {
-       {
-               .vendor         = PCI_VENDOR_ID_RICOH,
-               .device         = PCI_DEVICE_ID_RICOH_R5C843,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-       },
-       { /* end: all zeroes */ },
-};
-
-MODULE_DEVICE_TABLE(pci, pci_ids);
-
-static int ricoh_mmc_disable(struct pci_dev *fw_dev)
-{
-       u8 write_enable;
-       u8 write_target;
-       u8 disable;
-
-       if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
-               /* via RL5C476 */
-
-               pci_read_config_byte(fw_dev, 0xB7, &disable);
-               if (disable & 0x02) {
-                       printk(KERN_INFO DRIVER_NAME
-                               ": Controller already disabled. " \
-                               "Nothing to do.\n");
-                       return -ENODEV;
-               }
-
-               pci_read_config_byte(fw_dev, 0x8E, &write_enable);
-               pci_write_config_byte(fw_dev, 0x8E, 0xAA);
-               pci_read_config_byte(fw_dev, 0x8D, &write_target);
-               pci_write_config_byte(fw_dev, 0x8D, 0xB7);
-               pci_write_config_byte(fw_dev, 0xB7, disable | 0x02);
-               pci_write_config_byte(fw_dev, 0x8E, write_enable);
-               pci_write_config_byte(fw_dev, 0x8D, write_target);
-       } else {
-               /* via R5C832 */
-
-               pci_read_config_byte(fw_dev, 0xCB, &disable);
-               if (disable & 0x02) {
-                       printk(KERN_INFO DRIVER_NAME
-                              ": Controller already disabled. " \
-                               "Nothing to do.\n");
-                       return -ENODEV;
-               }
-
-               pci_read_config_byte(fw_dev, 0xCA, &write_enable);
-               pci_write_config_byte(fw_dev, 0xCA, 0x57);
-               pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
-               pci_write_config_byte(fw_dev, 0xCA, write_enable);
-       }
-
-       printk(KERN_INFO DRIVER_NAME
-              ": Controller is now disabled.\n");
-
-       return 0;
-}
-
-static int ricoh_mmc_enable(struct pci_dev *fw_dev)
-{
-       u8 write_enable;
-       u8 write_target;
-       u8 disable;
-
-       if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
-               /* via RL5C476 */
-
-               pci_read_config_byte(fw_dev, 0x8E, &write_enable);
-               pci_write_config_byte(fw_dev, 0x8E, 0xAA);
-               pci_read_config_byte(fw_dev, 0x8D, &write_target);
-               pci_write_config_byte(fw_dev, 0x8D, 0xB7);
-               pci_read_config_byte(fw_dev, 0xB7, &disable);
-               pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02);
-               pci_write_config_byte(fw_dev, 0x8E, write_enable);
-               pci_write_config_byte(fw_dev, 0x8D, write_target);
-       } else {
-               /* via R5C832 */
-
-               pci_read_config_byte(fw_dev, 0xCA, &write_enable);
-               pci_read_config_byte(fw_dev, 0xCB, &disable);
-               pci_write_config_byte(fw_dev, 0xCA, 0x57);
-               pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
-               pci_write_config_byte(fw_dev, 0xCA, write_enable);
-       }
-
-       printk(KERN_INFO DRIVER_NAME
-              ": Controller is now re-enabled.\n");
-
-       return 0;
-}
-
-static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
-                                    const struct pci_device_id *ent)
-{
-       u8 rev;
-       u8 ctrlfound = 0;
-
-       struct pci_dev *fw_dev = NULL;
-
-       BUG_ON(pdev == NULL);
-       BUG_ON(ent == NULL);
-
-       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
-
-       printk(KERN_INFO DRIVER_NAME
-               ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
-               pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
-               (int)rev);
-
-       while ((fw_dev =
-               pci_get_device(PCI_VENDOR_ID_RICOH,
-                       PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) {
-               if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
-                   PCI_FUNC(fw_dev->devfn) == 0 &&
-                   pdev->bus == fw_dev->bus) {
-                       if (ricoh_mmc_disable(fw_dev) != 0)
-                               return -ENODEV;
-
-                       pci_set_drvdata(pdev, fw_dev);
-
-                       ++ctrlfound;
-                       break;
-               }
-       }
-
-       fw_dev = NULL;
-
-       while (!ctrlfound &&
-           (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH,
-                                       PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
-               if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
-                   PCI_FUNC(fw_dev->devfn) == 0 &&
-                   pdev->bus == fw_dev->bus) {
-                       if (ricoh_mmc_disable(fw_dev) != 0)
-                               return -ENODEV;
-
-                       pci_set_drvdata(pdev, fw_dev);
-
-                       ++ctrlfound;
-               }
-       }
-
-       if (!ctrlfound) {
-               printk(KERN_WARNING DRIVER_NAME
-                      ": Main Ricoh function not found. Cannot disable controller.\n");
-               return -ENODEV;
-       }
-
-       return 0;
-}
-
-static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
-{
-       struct pci_dev *fw_dev = NULL;
-
-       fw_dev = pci_get_drvdata(pdev);
-       BUG_ON(fw_dev == NULL);
-
-       ricoh_mmc_enable(fw_dev);
-
-       pci_set_drvdata(pdev, NULL);
-}
-
-static int ricoh_mmc_suspend_late(struct pci_dev *pdev, pm_message_t state)
-{
-       struct pci_dev *fw_dev = NULL;
-
-       fw_dev = pci_get_drvdata(pdev);
-       BUG_ON(fw_dev == NULL);
-
-       printk(KERN_INFO DRIVER_NAME ": Suspending.\n");
-
-       ricoh_mmc_enable(fw_dev);
-
-       return 0;
-}
-
-static int ricoh_mmc_resume_early(struct pci_dev *pdev)
-{
-       struct pci_dev *fw_dev = NULL;
-
-       fw_dev = pci_get_drvdata(pdev);
-       BUG_ON(fw_dev == NULL);
-
-       printk(KERN_INFO DRIVER_NAME ": Resuming.\n");
-
-       ricoh_mmc_disable(fw_dev);
-
-       return 0;
-}
-
-static struct pci_driver ricoh_mmc_driver = {
-       .name =         DRIVER_NAME,
-       .id_table =     pci_ids,
-       .probe =        ricoh_mmc_probe,
-       .remove =       __devexit_p(ricoh_mmc_remove),
-       .suspend_late = ricoh_mmc_suspend_late,
-       .resume_early = ricoh_mmc_resume_early,
-};
-
-/*****************************************************************************\
- *                                                                           *
- * Driver init/exit                                                          *
- *                                                                           *
-\*****************************************************************************/
-
-static int __init ricoh_mmc_drv_init(void)
-{
-       printk(KERN_INFO DRIVER_NAME
-               ": Ricoh MMC Controller disabling driver\n");
-       printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
-
-       return pci_register_driver(&ricoh_mmc_driver);
-}
-
-static void __exit ricoh_mmc_drv_exit(void)
-{
-       pci_unregister_driver(&ricoh_mmc_driver);
-}
-
-module_init(ricoh_mmc_drv_init);
-module_exit(ricoh_mmc_drv_exit);
-
-MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
-MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
-MODULE_LICENSE("GPL");
-
index d96e1abf2d64a77fae5a52b0c82e9db8c697ecfc..2fdf7689ae6c3a2bdb2c876f0299b2c7e9009edf 100644 (file)
@@ -1179,7 +1179,7 @@ static int s3cmci_card_present(struct mmc_host *mmc)
        struct s3c24xx_mci_pdata *pdata = host->pdata;
        int ret;
 
-       if (pdata->gpio_detect == 0)
+       if (pdata->no_detect)
                return -ENOSYS;
 
        ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1;
@@ -1360,6 +1360,8 @@ static struct mmc_host_ops s3cmci_ops = {
 static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
        /* This is currently here to avoid a number of if (host->pdata)
         * checks. Any zero fields to ensure reasonable defaults are picked. */
+        .no_wprotect = 1,
+        .no_detect = 1,
 };
 
 #ifdef CONFIG_CPU_FREQ
index 5c3a1767770a2cb78d1c726abeaaec39a84113f7..8e1020cf73f42dfdeb85c1e9abe0a65f5b2b259f 100644 (file)
@@ -80,9 +80,6 @@ struct sdhci_pci_chip {
 
 static int ricoh_probe(struct sdhci_pci_chip *chip)
 {
-       if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
-               chip->quirks |= SDHCI_QUIRK_CLOCK_BEFORE_RESET;
-
        if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG ||
            chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY)
                chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET;
@@ -92,7 +89,9 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
 
 static const struct sdhci_pci_fixes sdhci_ricoh = {
        .probe          = ricoh_probe,
-       .quirks         = SDHCI_QUIRK_32BIT_DMA_ADDR,
+       .quirks         = SDHCI_QUIRK_32BIT_DMA_ADDR |
+                         SDHCI_QUIRK_FORCE_DMA |
+                         SDHCI_QUIRK_CLOCK_BEFORE_RESET,
 };
 
 static const struct sdhci_pci_fixes sdhci_ene_712 = {
@@ -501,6 +500,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
 {
        struct sdhci_pci_chip *chip;
        struct sdhci_pci_slot *slot;
+       mmc_pm_flag_t pm_flags = 0;
        int i, ret;
 
        chip = pci_get_drvdata(pdev);
@@ -519,6 +519,8 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
                                sdhci_resume_host(chip->slots[i]->host);
                        return ret;
                }
+
+               pm_flags |= slot->host->mmc->pm_flags;
        }
 
        if (chip->fixes && chip->fixes->suspend) {
@@ -531,9 +533,15 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
        }
 
        pci_save_state(pdev);
-       pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
-       pci_disable_device(pdev);
-       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       if (pm_flags & MMC_PM_KEEP_POWER) {
+               if (pm_flags & MMC_PM_WAKE_SDIO_IRQ)
+                       pci_enable_wake(pdev, PCI_D3hot, 1);
+               pci_set_power_state(pdev, PCI_D3hot);
+       } else {
+               pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+               pci_disable_device(pdev);
+               pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       }
 
        return 0;
 }
@@ -653,6 +661,8 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
                        goto unmap;
        }
 
+       host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
+
        ret = sdhci_add_host(host);
        if (ret)
                goto remove;
index c279fbc4c2e54059d3c06fcec327b59df287bb84..d6ab62d539fb2aa03f084eee2a305fcee3d5bd84 100644 (file)
@@ -174,20 +174,31 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
                sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
 }
 
-static void sdhci_init(struct sdhci_host *host)
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
+
+static void sdhci_init(struct sdhci_host *host, int soft)
 {
-       sdhci_reset(host, SDHCI_RESET_ALL);
+       if (soft)
+               sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+       else
+               sdhci_reset(host, SDHCI_RESET_ALL);
 
        sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
                SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
                SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
                SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
                SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
+
+       if (soft) {
+               /* force clock reconfiguration */
+               host->clock = 0;
+               sdhci_set_ios(host->mmc, &host->mmc->ios);
+       }
 }
 
 static void sdhci_reinit(struct sdhci_host *host)
 {
-       sdhci_init(host);
+       sdhci_init(host, 0);
        sdhci_enable_card_detection(host);
 }
 
@@ -376,6 +387,20 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
        local_irq_restore(*flags);
 }
 
+static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd)
+{
+       __le32 *dataddr = (__le32 __force *)(desc + 4);
+       __le16 *cmdlen = (__le16 __force *)desc;
+
+       /* SDHCI specification says ADMA descriptors should be 4 byte
+        * aligned, so using 16 or 32bit operations should be safe. */
+
+       cmdlen[0] = cpu_to_le16(cmd);
+       cmdlen[1] = cpu_to_le16(len);
+
+       dataddr[0] = cpu_to_le32(addr);
+}
+
 static int sdhci_adma_table_pre(struct sdhci_host *host,
        struct mmc_data *data)
 {
@@ -443,19 +468,11 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
                                sdhci_kunmap_atomic(buffer, &flags);
                        }
 
-                       desc[7] = (align_addr >> 24) & 0xff;
-                       desc[6] = (align_addr >> 16) & 0xff;
-                       desc[5] = (align_addr >> 8) & 0xff;
-                       desc[4] = (align_addr >> 0) & 0xff;
+                       /* tran, valid */
+                       sdhci_set_adma_desc(desc, align_addr, offset, 0x21);
 
                        BUG_ON(offset > 65536);
 
-                       desc[3] = (offset >> 8) & 0xff;
-                       desc[2] = (offset >> 0) & 0xff;
-
-                       desc[1] = 0x00;
-                       desc[0] = 0x21; /* tran, valid */
-
                        align += 4;
                        align_addr += 4;
 
@@ -465,19 +482,10 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
                        len -= offset;
                }
 
-               desc[7] = (addr >> 24) & 0xff;
-               desc[6] = (addr >> 16) & 0xff;
-               desc[5] = (addr >> 8) & 0xff;
-               desc[4] = (addr >> 0) & 0xff;
-
                BUG_ON(len > 65536);
 
-               desc[3] = (len >> 8) & 0xff;
-               desc[2] = (len >> 0) & 0xff;
-
-               desc[1] = 0x00;
-               desc[0] = 0x21; /* tran, valid */
-
+               /* tran, valid */
+               sdhci_set_adma_desc(desc, addr, len, 0x21);
                desc += 8;
 
                /*
@@ -490,16 +498,9 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
        /*
         * Add a terminating entry.
         */
-       desc[7] = 0;
-       desc[6] = 0;
-       desc[5] = 0;
-       desc[4] = 0;
 
-       desc[3] = 0;
-       desc[2] = 0;
-
-       desc[1] = 0x00;
-       desc[0] = 0x03; /* nop, end, valid */
+       /* nop, end, valid */
+       sdhci_set_adma_desc(desc, 0, 0, 0x3);
 
        /*
         * Resync align buffer as we might have changed it.
@@ -1610,16 +1611,13 @@ int sdhci_resume_host(struct sdhci_host *host)
        if (ret)
                return ret;
 
-       sdhci_init(host);
+       sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
        mmiowb();
 
        ret = mmc_resume_host(host->mmc);
-       if (ret)
-               return ret;
-
        sdhci_enable_card_detection(host);
 
-       return 0;
+       return ret;
 }
 
 EXPORT_SYMBOL_GPL(sdhci_resume_host);
@@ -1874,7 +1872,7 @@ int sdhci_add_host(struct sdhci_host *host)
        if (ret)
                goto untasklet;
 
-       sdhci_init(host);
+       sdhci_init(host, 0);
 
 #ifdef CONFIG_MMC_DEBUG
        sdhci_dumpregs(host);
index 14cec04c34f90c8adec61770e6607faa5f8540ab..bc45ef9af17d8f94b3cd2bed56e6771837296082 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/stringify.h>
+#include <linux/namei.h>
 #include <linux/stat.h>
 #include <linux/miscdevice.h>
 #include <linux/log2.h>
@@ -50,7 +51,8 @@
 
 /**
  * struct mtd_dev_param - MTD device parameter description data structure.
- * @name: MTD device name or number string
+ * @name: MTD character device node path, MTD device name, or MTD device number
+ *        string
  * @vid_hdr_offs: VID header offset
  */
 struct mtd_dev_param {
@@ -59,10 +61,10 @@ struct mtd_dev_param {
 };
 
 /* Numbers of elements set in the @mtd_dev_param array */
-static int mtd_devs;
+static int __initdata mtd_devs;
 
 /* MTD devices specification parameters */
-static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
+static struct mtd_dev_param __initdata mtd_dev_param[UBI_MAX_DEVICES];
 
 /* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
 struct class *ubi_class;
@@ -363,11 +365,13 @@ static void dev_release(struct device *dev)
 /**
  * ubi_sysfs_init - initialize sysfs for an UBI device.
  * @ubi: UBI device description object
+ * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was
+ *       taken
  *
  * This function returns zero in case of success and a negative error code in
  * case of failure.
  */
-static int ubi_sysfs_init(struct ubi_device *ubi)
+static int ubi_sysfs_init(struct ubi_device *ubi, int *ref)
 {
        int err;
 
@@ -379,6 +383,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi)
        if (err)
                return err;
 
+       *ref = 1;
        err = device_create_file(&ubi->dev, &dev_eraseblock_size);
        if (err)
                return err;
@@ -434,7 +439,7 @@ static void ubi_sysfs_close(struct ubi_device *ubi)
 }
 
 /**
- * kill_volumes - destroy all volumes.
+ * kill_volumes - destroy all user volumes.
  * @ubi: UBI device description object
  */
 static void kill_volumes(struct ubi_device *ubi)
@@ -446,37 +451,30 @@ static void kill_volumes(struct ubi_device *ubi)
                        ubi_free_volume(ubi, ubi->volumes[i]);
 }
 
-/**
- * free_user_volumes - free all user volumes.
- * @ubi: UBI device description object
- *
- * Normally the volumes are freed at the release function of the volume device
- * objects. However, on error paths the volumes have to be freed before the
- * device objects have been initialized.
- */
-static void free_user_volumes(struct ubi_device *ubi)
-{
-       int i;
-
-       for (i = 0; i < ubi->vtbl_slots; i++)
-               if (ubi->volumes[i]) {
-                       kfree(ubi->volumes[i]->eba_tbl);
-                       kfree(ubi->volumes[i]);
-               }
-}
-
 /**
  * uif_init - initialize user interfaces for an UBI device.
  * @ubi: UBI device description object
+ * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was
+ *       taken, otherwise set to %0
+ *
+ * This function initializes various user interfaces for an UBI device. If the
+ * initialization fails at an early stage, this function frees all the
+ * resources it allocated, returns an error, and @ref is set to %0. However,
+ * if the initialization fails after the UBI device was registered in the
+ * driver core subsystem, this function takes a reference to @ubi->dev, because
+ * otherwise the release function ('dev_release()') would free whole @ubi
+ * object. The @ref argument is set to %1 in this case. The caller has to put
+ * this reference.
  *
  * This function returns zero in case of success and a negative error code in
- * case of failure. Note, this function destroys all volumes if it fails.
+ * case of failure.
  */
-static int uif_init(struct ubi_device *ubi)
+static int uif_init(struct ubi_device *ubi, int *ref)
 {
        int i, err;
        dev_t dev;
 
+       *ref = 0;
        sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
 
        /*
@@ -504,7 +502,7 @@ static int uif_init(struct ubi_device *ubi)
                goto out_unreg;
        }
 
-       err = ubi_sysfs_init(ubi);
+       err = ubi_sysfs_init(ubi, ref);
        if (err)
                goto out_sysfs;
 
@@ -522,6 +520,8 @@ static int uif_init(struct ubi_device *ubi)
 out_volumes:
        kill_volumes(ubi);
 out_sysfs:
+       if (*ref)
+               get_device(&ubi->dev);
        ubi_sysfs_close(ubi);
        cdev_del(&ubi->cdev);
 out_unreg:
@@ -875,7 +875,7 @@ static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state,
 int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
 {
        struct ubi_device *ubi;
-       int i, err, do_free = 1;
+       int i, err, ref = 0;
 
        /*
         * Check if we already have the same MTD device attached.
@@ -975,9 +975,9 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
                        goto out_detach;
        }
 
-       err = uif_init(ubi);
+       err = uif_init(ubi, &ref);
        if (err)
-               goto out_nofree;
+               goto out_detach;
 
        ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
        if (IS_ERR(ubi->bgt_thread)) {
@@ -1025,12 +1025,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
 
 out_uif:
        uif_close(ubi);
-out_nofree:
-       do_free = 0;
 out_detach:
        ubi_wl_close(ubi);
-       if (do_free)
-               free_user_volumes(ubi);
        free_internal_volumes(ubi);
        vfree(ubi->vtbl);
 out_free:
@@ -1039,7 +1035,10 @@ out_free:
 #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
        vfree(ubi->dbg_peb_buf);
 #endif
-       kfree(ubi);
+       if (ref)
+               put_device(&ubi->dev);
+       else
+               kfree(ubi);
        return err;
 }
 
@@ -1096,7 +1095,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
 
        /*
         * Get a reference to the device in order to prevent 'dev_release()'
-        * from freeing @ubi object.
+        * from freeing the @ubi object.
         */
        get_device(&ubi->dev);
 
@@ -1116,13 +1115,50 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
 }
 
 /**
- * find_mtd_device - open an MTD device by its name or number.
- * @mtd_dev: name or number of the device
+ * open_mtd_by_chdev - open an MTD device by its character device node path.
+ * @mtd_dev: MTD character device node path
+ *
+ * This helper function opens an MTD device by its character node device path.
+ * Returns MTD device description object in case of success and a negative
+ * error code in case of failure.
+ */
+static struct mtd_info * __init open_mtd_by_chdev(const char *mtd_dev)
+{
+       int err, major, minor, mode;
+       struct path path;
+
+       /* Probably this is an MTD character device node path */
+       err = kern_path(mtd_dev, LOOKUP_FOLLOW, &path);
+       if (err)
+               return ERR_PTR(err);
+
+       /* MTD device number is defined by the major / minor numbers */
+       major = imajor(path.dentry->d_inode);
+       minor = iminor(path.dentry->d_inode);
+       mode = path.dentry->d_inode->i_mode;
+       path_put(&path);
+       if (major != MTD_CHAR_MAJOR || !S_ISCHR(mode))
+               return ERR_PTR(-EINVAL);
+
+       if (minor & 1)
+               /*
+                * Just do not think the "/dev/mtdrX" devices support is need,
+                * so do not support them to avoid doing extra work.
+                */
+               return ERR_PTR(-EINVAL);
+
+       return get_mtd_device(NULL, minor / 2);
+}
+
+/**
+ * open_mtd_device - open MTD device by name, character device path, or number.
+ * @mtd_dev: name, character device node path, or MTD device device number
  *
  * This function tries to open and MTD device described by @mtd_dev string,
- * which is first treated as an ASCII number, and if it is not true, it is
- * treated as MTD device name. Returns MTD device description object in case of
- * success and a negative error code in case of failure.
+ * which is first treated as ASCII MTD device number, and if it is not true, it
+ * is treated as MTD device name, and if that is also not true, it is treated
+ * as MTD character device node path. Returns MTD device description object in
+ * case of success and a negative error code in case of failure.
  */
 static struct mtd_info * __init open_mtd_device(const char *mtd_dev)
 {
@@ -1137,6 +1173,9 @@ static struct mtd_info * __init open_mtd_device(const char *mtd_dev)
                 * MTD device name.
                 */
                mtd = get_mtd_device_nm(mtd_dev);
+               if (IS_ERR(mtd) && PTR_ERR(mtd) == -ENODEV)
+                       /* Probably this is an MTD character device node path */
+                       mtd = open_mtd_by_chdev(mtd_dev);
        } else
                mtd = get_mtd_device(NULL, mtd_num);
 
@@ -1352,13 +1391,15 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
 
 module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000);
 MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: "
-                     "mtd=<name|num>[,<vid_hdr_offs>].\n"
+                     "mtd=<name|num|path>[,<vid_hdr_offs>].\n"
                      "Multiple \"mtd\" parameters may be specified.\n"
-                     "MTD devices may be specified by their number or name.\n"
+                     "MTD devices may be specified by their number, name, or "
+                     "path to the MTD character device node.\n"
                      "Optional \"vid_hdr_offs\" parameter specifies UBI VID "
-                     "header position and data starting position to be used "
-                     "by UBI.\n"
-                     "Example: mtd=content,1984 mtd=4 - attach MTD device"
+                     "header position to be used by UBI.\n"
+                     "Example 1: mtd=/dev/mtd0 - attach MTD device "
+                     "/dev/mtd0.\n"
+                     "Example 2: mtd=content,1984 mtd=4 - attach MTD device "
                      "with name \"content\" using VID header offset 1984, and "
                      "MTD device number 4 with default VID header offset.");
 
index f30bcb372c0502348198ff81f6c145f38bbe719b..17a1071297267b7a45e565f71f8bbedb7ab31df6 100644 (file)
@@ -96,8 +96,11 @@ void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
 
 #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
 int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len);
+int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
+                       int offset, int len);
 #else
 #define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0
+#define ubi_dbg_check_write(ubi, buf, pnum, offset, len) 0
 #endif
 
 #ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
@@ -176,6 +179,7 @@ static inline int ubi_dbg_is_erase_failure(void)
 #define ubi_dbg_is_write_failure() 0
 #define ubi_dbg_is_erase_failure() 0
 #define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0
+#define ubi_dbg_check_write(ubi, buf, pnum, offset, len) 0
 
 #endif /* !CONFIG_MTD_UBI_DEBUG */
 #endif /* !__UBI_DEBUG_H__ */
index 8aa51e7a6a7df515e25c628b767b18a2e3838c50..b4ecc84c75490dea7405dea6198b3e1614339585 100644 (file)
@@ -143,7 +143,7 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
 
        err = paranoid_check_not_bad(ubi, pnum);
        if (err)
-               return err > 0 ? -EINVAL : err;
+               return err;
 
        addr = (loff_t)pnum * ubi->peb_size + offset;
 retry:
@@ -236,12 +236,12 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
 
        err = paranoid_check_not_bad(ubi, pnum);
        if (err)
-               return err > 0 ? -EINVAL : err;
+               return err;
 
        /* The area we are writing to has to contain all 0xFF bytes */
        err = ubi_dbg_check_all_ff(ubi, pnum, offset, len);
        if (err)
-               return err > 0 ? -EINVAL : err;
+               return err;
 
        if (offset >= ubi->leb_start) {
                /*
@@ -250,10 +250,10 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
                 */
                err = paranoid_check_peb_ec_hdr(ubi, pnum);
                if (err)
-                       return err > 0 ? -EINVAL : err;
+                       return err;
                err = paranoid_check_peb_vid_hdr(ubi, pnum);
                if (err)
-                       return err > 0 ? -EINVAL : err;
+                       return err;
        }
 
        if (ubi_dbg_is_write_failure()) {
@@ -273,6 +273,21 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
        } else
                ubi_assert(written == len);
 
+       if (!err) {
+               err = ubi_dbg_check_write(ubi, buf, pnum, offset, len);
+               if (err)
+                       return err;
+
+               /*
+                * Since we always write sequentially, the rest of the PEB has
+                * to contain only 0xFF bytes.
+                */
+               offset += len;
+               len = ubi->peb_size - offset;
+               if (len)
+                       err = ubi_dbg_check_all_ff(ubi, pnum, offset, len);
+       }
+
        return err;
 }
 
@@ -348,7 +363,7 @@ retry:
 
        err = ubi_dbg_check_all_ff(ubi, pnum, 0, ubi->peb_size);
        if (err)
-               return err > 0 ? -EINVAL : err;
+               return err;
 
        if (ubi_dbg_is_erase_failure() && !err) {
                dbg_err("cannot erase PEB %d (emulated)", pnum);
@@ -542,7 +557,7 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
 
        err = paranoid_check_not_bad(ubi, pnum);
        if (err != 0)
-               return err > 0 ? -EINVAL : err;
+               return err;
 
        if (ubi->ro_mode) {
                ubi_err("read-only mode");
@@ -819,7 +834,7 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
 
        err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr);
        if (err)
-               return -EINVAL;
+               return err;
 
        err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
        return err;
@@ -1083,7 +1098,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
 
        err = paranoid_check_peb_ec_hdr(ubi, pnum);
        if (err)
-               return err > 0 ? -EINVAL : err;
+               return err;
 
        vid_hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC);
        vid_hdr->version = UBI_VERSION;
@@ -1092,7 +1107,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
 
        err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr);
        if (err)
-               return -EINVAL;
+               return err;
 
        p = (char *)vid_hdr - ubi->vid_hdr_shift;
        err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
@@ -1107,8 +1122,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
  * @ubi: UBI device description object
  * @pnum: physical eraseblock number to check
  *
- * This function returns zero if the physical eraseblock is good, a positive
- * number if it is bad and a negative error code if an error occurred.
+ * This function returns zero if the physical eraseblock is good, %-EINVAL if
+ * it is bad and a negative error code if an error occurred.
  */
 static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
 {
@@ -1120,7 +1135,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
 
        ubi_err("paranoid check failed for PEB %d", pnum);
        ubi_dbg_dump_stack();
-       return err;
+       return err > 0 ? -EINVAL : err;
 }
 
 /**
@@ -1130,7 +1145,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
  * @ec_hdr: the erase counter header to check
  *
  * This function returns zero if the erase counter header contains valid
- * values, and %1 if not.
+ * values, and %-EINVAL if not.
  */
 static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
                                 const struct ubi_ec_hdr *ec_hdr)
@@ -1156,7 +1171,7 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
 fail:
        ubi_dbg_dump_ec_hdr(ec_hdr);
        ubi_dbg_dump_stack();
-       return 1;
+       return -EINVAL;
 }
 
 /**
@@ -1164,8 +1179,8 @@ fail:
  * @ubi: UBI device description object
  * @pnum: the physical eraseblock number to check
  *
- * This function returns zero if the erase counter header is all right, %1 if
- * not, and a negative error code if an error occurred.
+ * This function returns zero if the erase counter header is all right and and
+ * a negative error code if not or if an error occurred.
  */
 static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
 {
@@ -1188,7 +1203,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
                ubi_err("paranoid check failed for PEB %d", pnum);
                ubi_dbg_dump_ec_hdr(ec_hdr);
                ubi_dbg_dump_stack();
-               err = 1;
+               err = -EINVAL;
                goto exit;
        }
 
@@ -1206,7 +1221,7 @@ exit:
  * @vid_hdr: the volume identifier header to check
  *
  * This function returns zero if the volume identifier header is all right, and
- * %1 if not.
+ * %-EINVAL if not.
  */
 static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
                                  const struct ubi_vid_hdr *vid_hdr)
@@ -1233,7 +1248,7 @@ fail:
        ubi_err("paranoid check failed for PEB %d", pnum);
        ubi_dbg_dump_vid_hdr(vid_hdr);
        ubi_dbg_dump_stack();
-       return 1;
+       return -EINVAL;
 
 }
 
@@ -1243,7 +1258,7 @@ fail:
  * @pnum: the physical eraseblock number to check
  *
  * This function returns zero if the volume identifier header is all right,
- * %1 if not, and a negative error code if an error occurred.
+ * and a negative error code if not or if an error occurred.
  */
 static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
 {
@@ -1270,7 +1285,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
                ubi_err("paranoid check failed for PEB %d", pnum);
                ubi_dbg_dump_vid_hdr(vid_hdr);
                ubi_dbg_dump_stack();
-               err = 1;
+               err = -EINVAL;
                goto exit;
        }
 
@@ -1281,6 +1296,61 @@ exit:
        return err;
 }
 
+/**
+ * ubi_dbg_check_write - make sure write succeeded.
+ * @ubi: UBI device description object
+ * @buf: buffer with data which were written
+ * @pnum: physical eraseblock number the data were written to
+ * @offset: offset within the physical eraseblock the data were written to
+ * @len: how many bytes were written
+ *
+ * This functions reads data which were recently written and compares it with
+ * the original data buffer - the data have to match. Returns zero if the data
+ * match and a negative error code if not or in case of failure.
+ */
+int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
+                       int offset, int len)
+{
+       int err, i;
+
+       mutex_lock(&ubi->dbg_buf_mutex);
+       err = ubi_io_read(ubi, ubi->dbg_peb_buf, pnum, offset, len);
+       if (err)
+               goto out_unlock;
+
+       for (i = 0; i < len; i++) {
+               uint8_t c = ((uint8_t *)buf)[i];
+               uint8_t c1 = ((uint8_t *)ubi->dbg_peb_buf)[i];
+               int dump_len;
+
+               if (c == c1)
+                       continue;
+
+               ubi_err("paranoid check failed for PEB %d:%d, len %d",
+                       pnum, offset, len);
+               ubi_msg("data differ at position %d", i);
+               dump_len = max_t(int, 128, len - i);
+               ubi_msg("hex dump of the original buffer from %d to %d",
+                       i, i + dump_len);
+               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+                              buf + i, dump_len, 1);
+               ubi_msg("hex dump of the read buffer from %d to %d",
+                       i, i + dump_len);
+               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+                              ubi->dbg_peb_buf + i, dump_len, 1);
+               ubi_dbg_dump_stack();
+               err = -EINVAL;
+               goto out_unlock;
+       }
+       mutex_unlock(&ubi->dbg_buf_mutex);
+
+       return 0;
+
+out_unlock:
+       mutex_unlock(&ubi->dbg_buf_mutex);
+       return err;
+}
+
 /**
  * ubi_dbg_check_all_ff - check that a region of flash is empty.
  * @ubi: UBI device description object
@@ -1289,8 +1359,8 @@ exit:
  * @len: the length of the region to check
  *
  * This function returns zero if only 0xFF bytes are present at offset
- * @offset of the physical eraseblock @pnum, %1 if not, and a negative error
- * code if an error occurred.
+ * @offset of the physical eraseblock @pnum, and a negative error code if not
+ * or if an error occurred.
  */
 int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
 {
@@ -1321,7 +1391,7 @@ fail:
        ubi_msg("hex dump of the %d-%d region", offset, offset + len);
        print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
                       ubi->dbg_peb_buf, len, 1);
-       err = 1;
+       err = -EINVAL;
 error:
        ubi_dbg_dump_stack();
        mutex_unlock(&ubi->dbg_buf_mutex);
index 90af61a2c3e4d1a1a5341d85bbfe246d4bbe3ec9..594184bbd56a816526129d432a9522306ac92e70 100644 (file)
@@ -974,11 +974,8 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
                        seb->ec = si->mean_ec;
 
        err = paranoid_check_si(ubi, si);
-       if (err) {
-               if (err > 0)
-                       err = -EINVAL;
+       if (err)
                goto out_vidh;
-       }
 
        ubi_free_vid_hdr(ubi, vidh);
        kfree(ech);
@@ -1086,8 +1083,8 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si)
  * @ubi: UBI device description object
  * @si: scanning information
  *
- * This function returns zero if the scanning information is all right, %1 if
- * not and a negative error code if an error occurred.
+ * This function returns zero if the scanning information is all right, and a
+ * negative error code if not or if an error occurred.
  */
 static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
 {
@@ -1346,7 +1343,7 @@ bad_vid_hdr:
 
 out:
        ubi_dbg_dump_stack();
-       return 1;
+       return -EINVAL;
 }
 
 #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
index 600c7229d5cf21b0d959ca2d7c1a86ccc676e4af..f64ddabd4ac864ab841041d79510261d2b448d57 100644 (file)
@@ -464,7 +464,7 @@ retry:
                                   ubi->peb_size - ubi->vid_hdr_aloffset);
        if (err) {
                ubi_err("new PEB %d does not contain all 0xFF bytes", e->pnum);
-               return err > 0 ? -EINVAL : err;
+               return err;
        }
 
        return e->pnum;
@@ -513,7 +513,7 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
        dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec);
 
        err = paranoid_check_ec(ubi, e->pnum, e->ec);
-       if (err > 0)
+       if (err)
                return -EINVAL;
 
        ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
@@ -1572,8 +1572,7 @@ void ubi_wl_close(struct ubi_device *ubi)
  * @ec: the erase counter to check
  *
  * This function returns zero if the erase counter of physical eraseblock @pnum
- * is equivalent to @ec, %1 if not, and a negative error code if an error
- * occurred.
+ * is equivalent to @ec, and a negative error code if not or if an error occurred.
  */
 static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
 {
@@ -1611,8 +1610,8 @@ out_free:
  * @e: the wear-leveling entry to check
  * @root: the root of the tree
  *
- * This function returns zero if @e is in the @root RB-tree and %1 if it is
- * not.
+ * This function returns zero if @e is in the @root RB-tree and %-EINVAL if it
+ * is not.
  */
 static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
                                     struct rb_root *root)
@@ -1623,7 +1622,7 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
        ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ",
                e->pnum, e->ec, root);
        ubi_dbg_dump_stack();
-       return 1;
+       return -EINVAL;
 }
 
 /**
@@ -1632,7 +1631,7 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
  * @ubi: UBI device description object
  * @e: the wear-leveling entry to check
  *
- * This function returns zero if @e is in @ubi->pq and %1 if it is not.
+ * This function returns zero if @e is in @ubi->pq and %-EINVAL if it is not.
  */
 static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e)
 {
@@ -1647,6 +1646,6 @@ static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e)
        ubi_err("paranoid check failed for PEB %d, EC %d, Protect queue",
                e->pnum, e->ec);
        ubi_dbg_dump_stack();
-       return 1;
+       return -EINVAL;
 }
 #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
index 6aa526ee909614ecaea55a5794e52a256a784e66..61a7b4351e7837dc77cf7fdc2193491fbe492e07 100644 (file)
@@ -998,7 +998,7 @@ static int gfar_probe(struct of_device *ofdev,
        }
 
        /* Need to reverse the bit maps as  bit_map's MSB is q0
-        * but, for_each_bit parses from right to left, which
+        * but, for_each_set_bit parses from right to left, which
         * basically reverses the queue numbers */
        for (i = 0; i< priv->num_grps; i++) {
                priv->gfargrp[i].tx_bit_map = reverse_bitmap(
@@ -1011,7 +1011,7 @@ static int gfar_probe(struct of_device *ofdev,
         * also assign queues to groups */
        for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) {
                priv->gfargrp[grp_idx].num_rx_queues = 0x0;
-               for_each_bit(i, &priv->gfargrp[grp_idx].rx_bit_map,
+               for_each_set_bit(i, &priv->gfargrp[grp_idx].rx_bit_map,
                                priv->num_rx_queues) {
                        priv->gfargrp[grp_idx].num_rx_queues++;
                        priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx];
@@ -1019,7 +1019,7 @@ static int gfar_probe(struct of_device *ofdev,
                        rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
                }
                priv->gfargrp[grp_idx].num_tx_queues = 0x0;
-               for_each_bit (i, &priv->gfargrp[grp_idx].tx_bit_map,
+               for_each_set_bit(i, &priv->gfargrp[grp_idx].tx_bit_map,
                                priv->num_tx_queues) {
                        priv->gfargrp[grp_idx].num_tx_queues++;
                        priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx];
@@ -1709,7 +1709,7 @@ void gfar_configure_coalescing(struct gfar_private *priv,
 
        if (priv->mode == MQ_MG_MODE) {
                baddr = &regs->txic0;
-               for_each_bit (i, &tx_mask, priv->num_tx_queues) {
+               for_each_set_bit(i, &tx_mask, priv->num_tx_queues) {
                        if (likely(priv->tx_queue[i]->txcoalescing)) {
                                gfar_write(baddr + i, 0);
                                gfar_write(baddr + i, priv->tx_queue[i]->txic);
@@ -1717,7 +1717,7 @@ void gfar_configure_coalescing(struct gfar_private *priv,
                }
 
                baddr = &regs->rxic0;
-               for_each_bit (i, &rx_mask, priv->num_rx_queues) {
+               for_each_set_bit(i, &rx_mask, priv->num_rx_queues) {
                        if (likely(priv->rx_queue[i]->rxcoalescing)) {
                                gfar_write(baddr + i, 0);
                                gfar_write(baddr + i, priv->rx_queue[i]->rxic);
@@ -2607,7 +2607,7 @@ static int gfar_poll(struct napi_struct *napi, int budget)
                budget_per_queue = left_over_budget/num_queues;
                left_over_budget = 0;
 
-               for_each_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
+               for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
                        if (test_bit(i, &serviced_queues))
                                continue;
                        rx_queue = priv->rx_queue[i];
index 45e3532b166f384c4290464f68f4e66a4988f67b..684af371462dc32975f955ef0e96ac8063c09b02 100644 (file)
@@ -1050,7 +1050,7 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter)
         */
        for (v_idx = 0; v_idx < q_vectors; v_idx++) {
                q_vector = adapter->q_vector[v_idx];
-               /* XXX for_each_bit(...) */
+               /* XXX for_each_set_bit(...) */
                r_idx = find_first_bit(q_vector->rxr_idx,
                                       adapter->num_rx_queues);
 
index 235b5fd4b8d43aa4e1bace3cd73c8bb2f7913823..ca653c49b765ecd44d9b45616bcf91c7e6372d48 100644 (file)
@@ -751,7 +751,7 @@ static void ixgbevf_configure_msix(struct ixgbevf_adapter *adapter)
         */
        for (v_idx = 0; v_idx < q_vectors; v_idx++) {
                q_vector = adapter->q_vector[v_idx];
-               /* XXX for_each_bit(...) */
+               /* XXX for_each_set_bit(...) */
                r_idx = find_first_bit(q_vector->rxr_idx,
                                       adapter->num_rx_queues);
 
index 8a964f1303676f110478623b4f62d6b5db676f66..a6452af9c6c5949bb7ab744e91fa6893c7afb455 100644 (file)
@@ -394,7 +394,7 @@ static void ar9170_tx_fake_ampdu_status(struct ar9170 *ar)
                ieee80211_tx_status_irqsafe(ar->hw, skb);
        }
 
-       for_each_bit(i, &queue_bitmap, BITS_PER_BYTE) {
+       for_each_set_bit(i, &queue_bitmap, BITS_PER_BYTE) {
 #ifdef AR9170_QUEUE_STOP_DEBUG
                printk(KERN_DEBUG "%s: wake queue %d\n",
                       wiphy_name(ar->hw->wiphy), i);
index be992ca41cf12f9d1eeb1e2a0b07e87bf4a8e35f..c29c994de0e252867eeba263a032fca5b4982f70 100644 (file)
@@ -89,7 +89,7 @@ static int iwm_debugfs_dbg_modules_write(void *data, u64 val)
        for (i = 0; i < __IWM_DM_NR; i++)
                iwm->dbg.dbg_module[i] = 0;
 
-       for_each_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
+       for_each_set_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
                iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level;
 
        return 0;
index ad8f7eabb5aa283f9c8954860b1198b37af13ecc..8456b4dbd14694dd22fbbfad751a07daeeac8533 100644 (file)
@@ -1116,7 +1116,7 @@ static int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf,
                return -EINVAL;
        }
 
-       for_each_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) {
+       for_each_set_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) {
                tid_info = &sta_info->tid_info[bit];
 
                mutex_lock(&tid_info->mutex);
index 0be1d50645ab0f3fde6f47b2c65b72e81ecfc316..caa1531337540dec37c71d6865da922702e9bf3d 100644 (file)
@@ -460,7 +460,7 @@ static int init_slot(int slot, struct eeprom_eisa_slot_info *es)
                               slot, id_string);
                        
                        print_eisa_id(id_string, es->eisa_slot_id);
-                       printk(" expected %s \n", id_string);
+                       printk(" expected %s\n", id_string);
                
                        return -1;      
                        
index a35c9c5b89e8b25d39896709a1c111dbc5a6e866..f7806d81f1e00a65ab5c2f549293744b70485113 100644 (file)
@@ -169,7 +169,7 @@ superio_init(struct pci_dev *pcidev)
        /* ...then properly fixup the USB to point at suckyio PIC */
        sio->usb_pdev->irq = superio_fixup_irq(sio->usb_pdev);
 
-       printk(KERN_INFO PFX "Found NS87560 Legacy I/O device at %s (IRQ %i) \n",
+       printk(KERN_INFO PFX "Found NS87560 Legacy I/O device at %s (IRQ %i)\n",
               pci_name(pdev), pdev->irq);
 
        pci_read_config_dword (pdev, SIO_SP1BAR, &sio->sp1_base);
index 039e87b7144264370c059865922e8f68ef8d9d34..81d19d5683ace216b2edd8728c0bde78f3fccf31 100644 (file)
@@ -2533,6 +2533,91 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1518, quirk_i82576_sriov);
 
 #endif /* CONFIG_PCI_IOV */
 
+/*
+ * This is a quirk for the Ricoh MMC controller found as a part of
+ * some mulifunction chips.
+
+ * This is very similiar and based on the ricoh_mmc driver written by
+ * Philip Langdale. Thank you for these magic sequences.
+ *
+ * These chips implement the four main memory card controllers (SD, MMC, MS, xD)
+ * and one or both of cardbus or firewire.
+ *
+ * It happens that they implement SD and MMC
+ * support as separate controllers (and PCI functions). The linux SDHCI
+ * driver supports MMC cards but the chip detects MMC cards in hardware
+ * and directs them to the MMC controller - so the SDHCI driver never sees
+ * them.
+ *
+ * To get around this, we must disable the useless MMC controller.
+ * At that point, the SDHCI controller will start seeing them
+ * It seems to be the case that the relevant PCI registers to deactivate the
+ * MMC controller live on PCI function 0, which might be the cardbus controller
+ * or the firewire controller, depending on the particular chip in question
+ *
+ * This has to be done early, because as soon as we disable the MMC controller
+ * other pci functions shift up one level, e.g. function #2 becomes function
+ * #1, and this will confuse the pci core.
+ */
+
+#ifdef CONFIG_MMC_RICOH_MMC
+static void ricoh_mmc_fixup_rl5c476(struct pci_dev *dev)
+{
+       /* disable via cardbus interface */
+       u8 write_enable;
+       u8 write_target;
+       u8 disable;
+
+       /* disable must be done via function #0 */
+       if (PCI_FUNC(dev->devfn))
+               return;
+
+       pci_read_config_byte(dev, 0xB7, &disable);
+       if (disable & 0x02)
+               return;
+
+       pci_read_config_byte(dev, 0x8E, &write_enable);
+       pci_write_config_byte(dev, 0x8E, 0xAA);
+       pci_read_config_byte(dev, 0x8D, &write_target);
+       pci_write_config_byte(dev, 0x8D, 0xB7);
+       pci_write_config_byte(dev, 0xB7, disable | 0x02);
+       pci_write_config_byte(dev, 0x8E, write_enable);
+       pci_write_config_byte(dev, 0x8D, write_target);
+
+       dev_notice(&dev->dev, "proprietary Ricoh MMC controller disabled (via cardbus function)\n");
+       dev_notice(&dev->dev, "MMC cards are now supported by standard SDHCI controller\n");
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476, ricoh_mmc_fixup_rl5c476);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476, ricoh_mmc_fixup_rl5c476);
+
+static void ricoh_mmc_fixup_r5c832(struct pci_dev *dev)
+{
+       /* disable via firewire interface */
+       u8 write_enable;
+       u8 disable;
+
+       /* disable must be done via function #0 */
+       if (PCI_FUNC(dev->devfn))
+               return;
+
+       pci_read_config_byte(dev, 0xCB, &disable);
+
+       if (disable & 0x02)
+               return;
+
+       pci_read_config_byte(dev, 0xCA, &write_enable);
+       pci_write_config_byte(dev, 0xCA, 0x57);
+       pci_write_config_byte(dev, 0xCB, disable | 0x02);
+       pci_write_config_byte(dev, 0xCA, write_enable);
+
+       dev_notice(&dev->dev, "proprietary Ricoh MMC controller disabled (via firewire function)\n");
+       dev_notice(&dev->dev, "MMC cards are now supported by standard SDHCI controller\n");
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832);
+#endif /*CONFIG_MMC_RICOH_MMC*/
+
+
 static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
                          struct pci_fixup *end)
 {
index 0a6601c76809bb90c041742c5f8ba437f9be6814..d189e4743e699711a5fa63fb98a5d7542f2053b4 100644 (file)
@@ -51,17 +51,23 @@ config PCMCIA_LOAD_CIS
 
 config PCMCIA_IOCTL
        bool "PCMCIA control ioctl (obsolete)"
-       depends on PCMCIA
+       depends on PCMCIA && ARM && !SMP && !PREEMPT
        default y
        help
          If you say Y here, the deprecated ioctl interface to the PCMCIA
-         subsystem will be built. It is needed by cardmgr and cardctl
-         (pcmcia-cs) to function properly.
+         subsystem will be built. It is needed by the deprecated pcmcia-cs
+         tools (cardmgr, cardctl) to function properly.
 
          You should use the new pcmciautils package instead (see
          <file:Documentation/Changes> for location and details).
 
-         If unsure, say Y.
+         This config option will most likely be removed from kernel 2.6.35,
+         the associated code from kernel 2.6.36.
+
+         As the PCMCIA ioctl is not locking safe, it depends on !SMP and
+         !PREEMPT.
+
+         If unsure, say N.
 
 config CARDBUS
        bool "32-bit CardBus support"
index ac0686efbf759de211fe1e78bb6ae5f58d2f0fca..e6ab2a47d8cbcabe33d4cfd2bedf604670269216 100644 (file)
@@ -71,7 +71,7 @@ int __ref cb_alloc(struct pcmcia_socket *s)
        unsigned int max, pass;
 
        s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0));
-       pci_fixup_cardbus(bus); 
+       pci_fixup_cardbus(bus);
 
        max = bus->secondary;
        for (pass = 0; pass < 2; pass++)
index 2f3622dd4b69b7f5d652ccb56a3e28168b2b4b23..f230f6543bffef6554b20289c445f3730d880d54 100644 (file)
@@ -54,46 +54,44 @@ static const u_int exponent[] = {
 /* Upper limit on reasonable # of tuples */
 #define MAX_TUPLES             200
 
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
 /* 16-bit CIS? */
 static int cis_width;
 module_param(cis_width, int, 0444);
 
 void release_cis_mem(struct pcmcia_socket *s)
 {
-    mutex_lock(&s->ops_mutex);
-    if (s->cis_mem.flags & MAP_ACTIVE) {
-       s->cis_mem.flags &= ~MAP_ACTIVE;
-       s->ops->set_mem_map(s, &s->cis_mem);
-       if (s->cis_mem.res) {
-           release_resource(s->cis_mem.res);
-           kfree(s->cis_mem.res);
-           s->cis_mem.res = NULL;
+       mutex_lock(&s->ops_mutex);
+       if (s->cis_mem.flags & MAP_ACTIVE) {
+               s->cis_mem.flags &= ~MAP_ACTIVE;
+               s->ops->set_mem_map(s, &s->cis_mem);
+               if (s->cis_mem.res) {
+                       release_resource(s->cis_mem.res);
+                       kfree(s->cis_mem.res);
+                       s->cis_mem.res = NULL;
+               }
+               iounmap(s->cis_virt);
+               s->cis_virt = NULL;
        }
-       iounmap(s->cis_virt);
-       s->cis_virt = NULL;
-    }
-    mutex_unlock(&s->ops_mutex);
+       mutex_unlock(&s->ops_mutex);
 }
 
-/*
- * Map the card memory at "card_offset" into virtual space.
+/**
+ * set_cis_map() - map the card memory at "card_offset" into virtual space.
+ *
  * If flags & MAP_ATTRIB, map the attribute space, otherwise
  * map the memory space.
  *
  * Must be called with ops_mutex held.
  */
-static void __iomem *
-set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
+static void __iomem *set_cis_map(struct pcmcia_socket *s,
+                               unsigned int card_offset, unsigned int flags)
 {
        pccard_mem_map *mem = &s->cis_mem;
        int ret;
 
        if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
-               mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s);
+               mem->res = pcmcia_find_mem_region(0, s->map_size,
+                                               s->map_size, 0, s);
                if (mem->res == NULL) {
                        dev_printk(KERN_NOTICE, &s->dev,
                                   "cs: unable to map card memory!\n");
@@ -124,165 +122,170 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag
        return s->cis_virt;
 }
 
-/*======================================================================
-
-    Low-level functions to read and write CIS memory.  I think the
-    write routine is only useful for writing one-byte registers.
-
-======================================================================*/
 
 /* Bits in attr field */
 #define IS_ATTR                1
 #define IS_INDIRECT    8
 
+/**
+ * pcmcia_read_cis_mem() - low-level function to read CIS memory
+ */
 int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
                 u_int len, void *ptr)
 {
-    void __iomem *sys, *end;
-    unsigned char *buf = ptr;
-
-    dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
-
-    mutex_lock(&s->ops_mutex);
-    if (attr & IS_INDIRECT) {
-       /* Indirect accesses use a bunch of special registers at fixed
-          locations in common memory */
-       u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
-       if (attr & IS_ATTR) {
-           addr *= 2;
-           flags = ICTRL0_AUTOINC;
-       }
+       void __iomem *sys, *end;
+       unsigned char *buf = ptr;
 
-       sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
-       if (!sys) {
-           dev_dbg(&s->dev, "could not map memory\n");
-           memset(ptr, 0xff, len);
-           mutex_unlock(&s->ops_mutex);
-           return -1;
-       }
+       dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
 
-       writeb(flags, sys+CISREG_ICTRL0);
-       writeb(addr & 0xff, sys+CISREG_IADDR0);
-       writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
-       writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
-       writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
-       for ( ; len > 0; len--, buf++)
-           *buf = readb(sys+CISREG_IDATA0);
-    } else {
-       u_int inc = 1, card_offset, flags;
-
-       if (addr > CISTPL_MAX_CIS_SIZE)
-               dev_dbg(&s->dev, "attempt to read CIS mem at addr %#x", addr);
-
-       flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
-       if (attr) {
-           flags |= MAP_ATTRIB;
-           inc++;
-           addr *= 2;
-       }
+       mutex_lock(&s->ops_mutex);
+       if (attr & IS_INDIRECT) {
+               /* Indirect accesses use a bunch of special registers at fixed
+                  locations in common memory */
+               u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
+               if (attr & IS_ATTR) {
+                       addr *= 2;
+                       flags = ICTRL0_AUTOINC;
+               }
 
-       card_offset = addr & ~(s->map_size-1);
-       while (len) {
-           sys = set_cis_map(s, card_offset, flags);
-           if (!sys) {
-               dev_dbg(&s->dev, "could not map memory\n");
-               memset(ptr, 0xff, len);
-               mutex_unlock(&s->ops_mutex);
-               return -1;
-           }
-           end = sys + s->map_size;
-           sys = sys + (addr & (s->map_size-1));
-           for ( ; len > 0; len--, buf++, sys += inc) {
-               if (sys == end)
-                   break;
-               *buf = readb(sys);
-           }
-           card_offset += s->map_size;
-           addr = 0;
+               sys = set_cis_map(s, 0, MAP_ACTIVE |
+                               ((cis_width) ? MAP_16BIT : 0));
+               if (!sys) {
+                       dev_dbg(&s->dev, "could not map memory\n");
+                       memset(ptr, 0xff, len);
+                       mutex_unlock(&s->ops_mutex);
+                       return -1;
+               }
+
+               writeb(flags, sys+CISREG_ICTRL0);
+               writeb(addr & 0xff, sys+CISREG_IADDR0);
+               writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
+               writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
+               writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
+               for ( ; len > 0; len--, buf++)
+                       *buf = readb(sys+CISREG_IDATA0);
+       } else {
+               u_int inc = 1, card_offset, flags;
+
+               if (addr > CISTPL_MAX_CIS_SIZE)
+                       dev_dbg(&s->dev,
+                               "attempt to read CIS mem at addr %#x", addr);
+
+               flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
+               if (attr) {
+                       flags |= MAP_ATTRIB;
+                       inc++;
+                       addr *= 2;
+               }
+
+               card_offset = addr & ~(s->map_size-1);
+               while (len) {
+                       sys = set_cis_map(s, card_offset, flags);
+                       if (!sys) {
+                               dev_dbg(&s->dev, "could not map memory\n");
+                               memset(ptr, 0xff, len);
+                               mutex_unlock(&s->ops_mutex);
+                               return -1;
+                       }
+                       end = sys + s->map_size;
+                       sys = sys + (addr & (s->map_size-1));
+                       for ( ; len > 0; len--, buf++, sys += inc) {
+                               if (sys == end)
+                                       break;
+                               *buf = readb(sys);
+                       }
+                       card_offset += s->map_size;
+                       addr = 0;
+               }
        }
-    }
-    mutex_unlock(&s->ops_mutex);
-    dev_dbg(&s->dev, "  %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
-         *(u_char *)(ptr+0), *(u_char *)(ptr+1),
-         *(u_char *)(ptr+2), *(u_char *)(ptr+3));
-    return 0;
+       mutex_unlock(&s->ops_mutex);
+       dev_dbg(&s->dev, "  %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
+               *(u_char *)(ptr+0), *(u_char *)(ptr+1),
+               *(u_char *)(ptr+2), *(u_char *)(ptr+3));
+       return 0;
 }
 
 
+/**
+ * pcmcia_write_cis_mem() - low-level function to write CIS memory
+ *
+ * Probably only useful for writing one-byte registers.
+ */
 void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
                   u_int len, void *ptr)
 {
-    void __iomem *sys, *end;
-    unsigned char *buf = ptr;
-
-    dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
-
-    mutex_lock(&s->ops_mutex);
-    if (attr & IS_INDIRECT) {
-       /* Indirect accesses use a bunch of special registers at fixed
-          locations in common memory */
-       u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
-       if (attr & IS_ATTR) {
-           addr *= 2;
-           flags = ICTRL0_AUTOINC;
-       }
+       void __iomem *sys, *end;
+       unsigned char *buf = ptr;
 
-       sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
-       if (!sys) {
-               dev_dbg(&s->dev, "could not map memory\n");
-               mutex_unlock(&s->ops_mutex);
-               return; /* FIXME: Error */
-       }
+       dev_dbg(&s->dev,
+               "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
 
-       writeb(flags, sys+CISREG_ICTRL0);
-       writeb(addr & 0xff, sys+CISREG_IADDR0);
-       writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
-       writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
-       writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
-       for ( ; len > 0; len--, buf++)
-           writeb(*buf, sys+CISREG_IDATA0);
-    } else {
-       u_int inc = 1, card_offset, flags;
-
-       flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
-       if (attr & IS_ATTR) {
-           flags |= MAP_ATTRIB;
-           inc++;
-           addr *= 2;
-       }
+       mutex_lock(&s->ops_mutex);
+       if (attr & IS_INDIRECT) {
+               /* Indirect accesses use a bunch of special registers at fixed
+                  locations in common memory */
+               u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
+               if (attr & IS_ATTR) {
+                       addr *= 2;
+                       flags = ICTRL0_AUTOINC;
+               }
 
-       card_offset = addr & ~(s->map_size-1);
-       while (len) {
-           sys = set_cis_map(s, card_offset, flags);
-           if (!sys) {
-               dev_dbg(&s->dev, "could not map memory\n");
-               mutex_unlock(&s->ops_mutex);
-               return; /* FIXME: error */
-           }
-
-           end = sys + s->map_size;
-           sys = sys + (addr & (s->map_size-1));
-           for ( ; len > 0; len--, buf++, sys += inc) {
-               if (sys == end)
-                   break;
-               writeb(*buf, sys);
-           }
-           card_offset += s->map_size;
-           addr = 0;
-       }
-    }
-    mutex_unlock(&s->ops_mutex);
-}
+               sys = set_cis_map(s, 0, MAP_ACTIVE |
+                               ((cis_width) ? MAP_16BIT : 0));
+               if (!sys) {
+                       dev_dbg(&s->dev, "could not map memory\n");
+                       mutex_unlock(&s->ops_mutex);
+                       return; /* FIXME: Error */
+               }
+
+               writeb(flags, sys+CISREG_ICTRL0);
+               writeb(addr & 0xff, sys+CISREG_IADDR0);
+               writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
+               writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
+               writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
+               for ( ; len > 0; len--, buf++)
+                       writeb(*buf, sys+CISREG_IDATA0);
+       } else {
+               u_int inc = 1, card_offset, flags;
 
+               flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
+               if (attr & IS_ATTR) {
+                       flags |= MAP_ATTRIB;
+                       inc++;
+                       addr *= 2;
+               }
 
-/*======================================================================
+               card_offset = addr & ~(s->map_size-1);
+               while (len) {
+                       sys = set_cis_map(s, card_offset, flags);
+                       if (!sys) {
+                               dev_dbg(&s->dev, "could not map memory\n");
+                               mutex_unlock(&s->ops_mutex);
+                               return; /* FIXME: error */
+                       }
 
-    This is a wrapper around read_cis_mem, with the same interface,
-    but which caches information, for cards whose CIS may not be
-    readable all the time.
+                       end = sys + s->map_size;
+                       sys = sys + (addr & (s->map_size-1));
+                       for ( ; len > 0; len--, buf++, sys += inc) {
+                               if (sys == end)
+                                       break;
+                               writeb(*buf, sys);
+                       }
+                       card_offset += s->map_size;
+                       addr = 0;
+               }
+       }
+       mutex_unlock(&s->ops_mutex);
+}
 
-======================================================================*/
 
+/**
+ * read_cis_cache() - read CIS memory or its associated cache
+ *
+ * This is a wrapper around read_cis_mem, with the same interface,
+ * but which caches information, for cards whose CIS may not be
+ * readable all the time.
+ */
 static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
                        size_t len, void *ptr)
 {
@@ -353,7 +356,6 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
  * This destroys the CIS cache but keeps any fake CIS alive. Must be
  * called with ops_mutex held.
  */
-
 void destroy_cis_cache(struct pcmcia_socket *s)
 {
        struct list_head *l, *n;
@@ -366,13 +368,9 @@ void destroy_cis_cache(struct pcmcia_socket *s)
        }
 }
 
-/*======================================================================
-
-    This verifies if the CIS of a card matches what is in the CIS
-    cache.
-
-======================================================================*/
-
+/**
+ * verify_cis_cache() - does the CIS match what is in the CIS cache?
+ */
 int verify_cis_cache(struct pcmcia_socket *s)
 {
        struct cis_cache_entry *cis;
@@ -404,13 +402,12 @@ int verify_cis_cache(struct pcmcia_socket *s)
        return 0;
 }
 
-/*======================================================================
-
-    For really bad cards, we provide a facility for uploading a
-    replacement CIS.
-
-======================================================================*/
-
+/**
+ * pcmcia_replace_cis() - use a replacement CIS instead of the card's CIS
+ *
+ * For really bad cards, we provide a facility for uploading a
+ * replacement CIS.
+ */
 int pcmcia_replace_cis(struct pcmcia_socket *s,
                       const u8 *data, const size_t len)
 {
@@ -433,17 +430,13 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
        return 0;
 }
 
-/*======================================================================
-
-    The high-level CIS tuple services
-
-======================================================================*/
+/* The high-level CIS tuple services */
 
 typedef struct tuple_flags {
-    u_int              link_space:4;
-    u_int              has_link:1;
-    u_int              mfc_fn:3;
-    u_int              space:4;
+       u_int           link_space:4;
+       u_int           has_link:1;
+       u_int           mfc_fn:3;
+       u_int           space:4;
 } tuple_flags;
 
 #define LINK_SPACE(f)  (((tuple_flags *)(&(f)))->link_space)
@@ -451,982 +444,961 @@ typedef struct tuple_flags {
 #define MFC_FN(f)      (((tuple_flags *)(&(f)))->mfc_fn)
 #define SPACE(f)       (((tuple_flags *)(&(f)))->space)
 
-int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
+int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
+                       tuple_t *tuple)
 {
-    if (!s)
-       return -EINVAL;
-
-    if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
-       return -ENODEV;
-    tuple->TupleLink = tuple->Flags = 0;
-
-    /* Assume presence of a LONGLINK_C to address 0 */
-    tuple->CISOffset = tuple->LinkOffset = 0;
-    SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
-
-    if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
-       cisdata_t req = tuple->DesiredTuple;
-       tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
-       if (pccard_get_next_tuple(s, function, tuple) == 0) {
-           tuple->DesiredTuple = CISTPL_LINKTARGET;
-           if (pccard_get_next_tuple(s, function, tuple) != 0)
-               return -ENOSPC;
-       } else
-           tuple->CISOffset = tuple->TupleLink = 0;
-       tuple->DesiredTuple = req;
-    }
-    return pccard_get_next_tuple(s, function, tuple);
+       if (!s)
+               return -EINVAL;
+
+       if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
+               return -ENODEV;
+       tuple->TupleLink = tuple->Flags = 0;
+
+       /* Assume presence of a LONGLINK_C to address 0 */
+       tuple->CISOffset = tuple->LinkOffset = 0;
+       SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
+
+       if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
+               cisdata_t req = tuple->DesiredTuple;
+               tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
+               if (pccard_get_next_tuple(s, function, tuple) == 0) {
+                       tuple->DesiredTuple = CISTPL_LINKTARGET;
+                       if (pccard_get_next_tuple(s, function, tuple) != 0)
+                               return -ENOSPC;
+               } else
+                       tuple->CISOffset = tuple->TupleLink = 0;
+               tuple->DesiredTuple = req;
+       }
+       return pccard_get_next_tuple(s, function, tuple);
 }
 
 static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
 {
-    u_char link[5];
-    u_int ofs;
-    int ret;
-
-    if (MFC_FN(tuple->Flags)) {
-       /* Get indirect link from the MFC tuple */
-       ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
-                      tuple->LinkOffset, 5, link);
-       if (ret)
+       u_char link[5];
+       u_int ofs;
+       int ret;
+
+       if (MFC_FN(tuple->Flags)) {
+               /* Get indirect link from the MFC tuple */
+               ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
+                               tuple->LinkOffset, 5, link);
+               if (ret)
+                       return -1;
+               ofs = get_unaligned_le32(link + 1);
+               SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
+               /* Move to the next indirect link */
+               tuple->LinkOffset += 5;
+               MFC_FN(tuple->Flags)--;
+       } else if (HAS_LINK(tuple->Flags)) {
+               ofs = tuple->LinkOffset;
+               SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
+               HAS_LINK(tuple->Flags) = 0;
+       } else
                return -1;
-       ofs = get_unaligned_le32(link + 1);
-       SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
-       /* Move to the next indirect link */
-       tuple->LinkOffset += 5;
-       MFC_FN(tuple->Flags)--;
-    } else if (HAS_LINK(tuple->Flags)) {
-       ofs = tuple->LinkOffset;
-       SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
-       HAS_LINK(tuple->Flags) = 0;
-    } else {
-       return -1;
-    }
-    if (SPACE(tuple->Flags)) {
-       /* This is ugly, but a common CIS error is to code the long
-          link offset incorrectly, so we check the right spot... */
+
+       if (SPACE(tuple->Flags)) {
+               /* This is ugly, but a common CIS error is to code the long
+                  link offset incorrectly, so we check the right spot... */
+               ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+               if (ret)
+                       return -1;
+               if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
+                       (strncmp(link+2, "CIS", 3) == 0))
+                       return ofs;
+               remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
+               /* Then, we try the wrong spot... */
+               ofs = ofs >> 1;
+       }
        ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
        if (ret)
                return -1;
        if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
-           (strncmp(link+2, "CIS", 3) == 0))
-           return ofs;
+               (strncmp(link+2, "CIS", 3) == 0))
+               return ofs;
        remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
-       /* Then, we try the wrong spot... */
-       ofs = ofs >> 1;
-    }
-    ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
-    if (ret)
-           return -1;
-    if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
-       (strncmp(link+2, "CIS", 3) == 0))
-       return ofs;
-    remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
-    return -1;
+       return -1;
 }
 
-int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
+int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
+                       tuple_t *tuple)
 {
-    u_char link[2], tmp;
-    int ofs, i, attr;
-    int ret;
-
-    if (!s)
-       return -EINVAL;
-    if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
-       return -ENODEV;
-
-    link[1] = tuple->TupleLink;
-    ofs = tuple->CISOffset + tuple->TupleLink;
-    attr = SPACE(tuple->Flags);
-
-    for (i = 0; i < MAX_TUPLES; i++) {
-       if (link[1] == 0xff) {
-           link[0] = CISTPL_END;
-       } else {
-           ret = read_cis_cache(s, attr, ofs, 2, link);
-           if (ret)
-                   return -1;
-           if (link[0] == CISTPL_NULL) {
-               ofs++; continue;
-           }
-       }
+       u_char link[2], tmp;
+       int ofs, i, attr;
+       int ret;
 
-       /* End of chain?  Follow long link if possible */
-       if (link[0] == CISTPL_END) {
-           ofs = follow_link(s, tuple);
-           if (ofs < 0)
-               return -ENOSPC;
-           attr = SPACE(tuple->Flags);
-           ret = read_cis_cache(s, attr, ofs, 2, link);
-           if (ret)
-                   return -1;
-       }
+       if (!s)
+               return -EINVAL;
+       if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
+               return -ENODEV;
 
-       /* Is this a link tuple?  Make a note of it */
-       if ((link[0] == CISTPL_LONGLINK_A) ||
-           (link[0] == CISTPL_LONGLINK_C) ||
-           (link[0] == CISTPL_LONGLINK_MFC) ||
-           (link[0] == CISTPL_LINKTARGET) ||
-           (link[0] == CISTPL_INDIRECT) ||
-           (link[0] == CISTPL_NO_LINK)) {
-           switch (link[0]) {
-           case CISTPL_LONGLINK_A:
-               HAS_LINK(tuple->Flags) = 1;
-               LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
-               ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
-               if (ret)
-                       return -1;
-               break;
-           case CISTPL_LONGLINK_C:
-               HAS_LINK(tuple->Flags) = 1;
-               LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
-               ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
-               if (ret)
-                       return -1;
-               break;
-           case CISTPL_INDIRECT:
-               HAS_LINK(tuple->Flags) = 1;
-               LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT;
-               tuple->LinkOffset = 0;
-               break;
-           case CISTPL_LONGLINK_MFC:
-               tuple->LinkOffset = ofs + 3;
-               LINK_SPACE(tuple->Flags) = attr;
-               if (function == BIND_FN_ALL) {
-                   /* Follow all the MFC links */
-                   ret = read_cis_cache(s, attr, ofs+2, 1, &tmp);
-                   if (ret)
-                           return -1;
-                   MFC_FN(tuple->Flags) = tmp;
-               } else {
-                   /* Follow exactly one of the links */
-                   MFC_FN(tuple->Flags) = 1;
-                   tuple->LinkOffset += function * 5;
+       link[1] = tuple->TupleLink;
+       ofs = tuple->CISOffset + tuple->TupleLink;
+       attr = SPACE(tuple->Flags);
+
+       for (i = 0; i < MAX_TUPLES; i++) {
+               if (link[1] == 0xff)
+                       link[0] = CISTPL_END;
+               else {
+                       ret = read_cis_cache(s, attr, ofs, 2, link);
+                       if (ret)
+                               return -1;
+                       if (link[0] == CISTPL_NULL) {
+                               ofs++;
+                               continue;
+                       }
                }
-               break;
-           case CISTPL_NO_LINK:
-               HAS_LINK(tuple->Flags) = 0;
-               break;
-           }
-           if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
-               (tuple->DesiredTuple == RETURN_FIRST_TUPLE))
-               break;
-       } else
-           if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
-               break;
 
-       if (link[0] == tuple->DesiredTuple)
-           break;
-       ofs += link[1] + 2;
-    }
-    if (i == MAX_TUPLES) {
-       dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
-       return -ENOSPC;
-    }
-
-    tuple->TupleCode = link[0];
-    tuple->TupleLink = link[1];
-    tuple->CISOffset = ofs + 2;
-    return 0;
-}
+               /* End of chain?  Follow long link if possible */
+               if (link[0] == CISTPL_END) {
+                       ofs = follow_link(s, tuple);
+                       if (ofs < 0)
+                               return -ENOSPC;
+                       attr = SPACE(tuple->Flags);
+                       ret = read_cis_cache(s, attr, ofs, 2, link);
+                       if (ret)
+                               return -1;
+               }
 
-/*====================================================================*/
+               /* Is this a link tuple?  Make a note of it */
+               if ((link[0] == CISTPL_LONGLINK_A) ||
+                       (link[0] == CISTPL_LONGLINK_C) ||
+                       (link[0] == CISTPL_LONGLINK_MFC) ||
+                       (link[0] == CISTPL_LINKTARGET) ||
+                       (link[0] == CISTPL_INDIRECT) ||
+                       (link[0] == CISTPL_NO_LINK)) {
+                       switch (link[0]) {
+                       case CISTPL_LONGLINK_A:
+                               HAS_LINK(tuple->Flags) = 1;
+                               LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
+                               ret = read_cis_cache(s, attr, ofs+2, 4,
+                                               &tuple->LinkOffset);
+                               if (ret)
+                                       return -1;
+                               break;
+                       case CISTPL_LONGLINK_C:
+                               HAS_LINK(tuple->Flags) = 1;
+                               LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
+                               ret = read_cis_cache(s, attr, ofs+2, 4,
+                                               &tuple->LinkOffset);
+                               if (ret)
+                                       return -1;
+                               break;
+                       case CISTPL_INDIRECT:
+                               HAS_LINK(tuple->Flags) = 1;
+                               LINK_SPACE(tuple->Flags) = IS_ATTR |
+                                       IS_INDIRECT;
+                               tuple->LinkOffset = 0;
+                               break;
+                       case CISTPL_LONGLINK_MFC:
+                               tuple->LinkOffset = ofs + 3;
+                               LINK_SPACE(tuple->Flags) = attr;
+                               if (function == BIND_FN_ALL) {
+                                       /* Follow all the MFC links */
+                                       ret = read_cis_cache(s, attr, ofs+2,
+                                                       1, &tmp);
+                                       if (ret)
+                                               return -1;
+                                       MFC_FN(tuple->Flags) = tmp;
+                               } else {
+                                       /* Follow exactly one of the links */
+                                       MFC_FN(tuple->Flags) = 1;
+                                       tuple->LinkOffset += function * 5;
+                               }
+                               break;
+                       case CISTPL_NO_LINK:
+                               HAS_LINK(tuple->Flags) = 0;
+                               break;
+                       }
+                       if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
+                               (tuple->DesiredTuple == RETURN_FIRST_TUPLE))
+                               break;
+               } else
+                       if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
+                               break;
+
+               if (link[0] == tuple->DesiredTuple)
+                       break;
+               ofs += link[1] + 2;
+       }
+       if (i == MAX_TUPLES) {
+               dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
+               return -ENOSPC;
+       }
 
-#define _MIN(a, b)             (((a) < (b)) ? (a) : (b))
+       tuple->TupleCode = link[0];
+       tuple->TupleLink = link[1];
+       tuple->CISOffset = ofs + 2;
+       return 0;
+}
 
 int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
 {
-    u_int len;
-    int ret;
+       u_int len;
+       int ret;
 
-    if (!s)
-       return -EINVAL;
+       if (!s)
+               return -EINVAL;
 
-    if (tuple->TupleLink < tuple->TupleOffset)
-       return -ENOSPC;
-    len = tuple->TupleLink - tuple->TupleOffset;
-    tuple->TupleDataLen = tuple->TupleLink;
-    if (len == 0)
+       if (tuple->TupleLink < tuple->TupleOffset)
+               return -ENOSPC;
+       len = tuple->TupleLink - tuple->TupleOffset;
+       tuple->TupleDataLen = tuple->TupleLink;
+       if (len == 0)
+               return 0;
+       ret = read_cis_cache(s, SPACE(tuple->Flags),
+                       tuple->CISOffset + tuple->TupleOffset,
+                       min(len, (u_int) tuple->TupleDataMax),
+                       tuple->TupleData);
+       if (ret)
+               return -1;
        return 0;
-    ret = read_cis_cache(s, SPACE(tuple->Flags),
-                  tuple->CISOffset + tuple->TupleOffset,
-                  _MIN(len, tuple->TupleDataMax), tuple->TupleData);
-    if (ret)
-           return -1;
-    return 0;
 }
 
 
-/*======================================================================
-
-    Parsing routines for individual tuples
-
-======================================================================*/
+/* Parsing routines for individual tuples */
 
 static int parse_device(tuple_t *tuple, cistpl_device_t *device)
 {
-    int i;
-    u_char scale;
-    u_char *p, *q;
+       int i;
+       u_char scale;
+       u_char *p, *q;
 
-    p = (u_char *)tuple->TupleData;
-    q = p + tuple->TupleDataLen;
+       p = (u_char *)tuple->TupleData;
+       q = p + tuple->TupleDataLen;
 
-    device->ndev = 0;
-    for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
+       device->ndev = 0;
+       for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
 
-       if (*p == 0xff)
-               break;
-       device->dev[i].type = (*p >> 4);
-       device->dev[i].wp = (*p & 0x08) ? 1 : 0;
-       switch (*p & 0x07) {
-       case 0:
-               device->dev[i].speed = 0;
-               break;
-       case 1:
-               device->dev[i].speed = 250;
-               break;
-       case 2:
-               device->dev[i].speed = 200;
-               break;
-       case 3:
-               device->dev[i].speed = 150;
-               break;
-       case 4:
-               device->dev[i].speed = 100;
-               break;
-       case 7:
-               if (++p == q)
-                       return -EINVAL;
-               device->dev[i].speed = SPEED_CVT(*p);
-               while (*p & 0x80)
+               if (*p == 0xff)
+                       break;
+               device->dev[i].type = (*p >> 4);
+               device->dev[i].wp = (*p & 0x08) ? 1 : 0;
+               switch (*p & 0x07) {
+               case 0:
+                       device->dev[i].speed = 0;
+                       break;
+               case 1:
+                       device->dev[i].speed = 250;
+                       break;
+               case 2:
+                       device->dev[i].speed = 200;
+                       break;
+               case 3:
+                       device->dev[i].speed = 150;
+                       break;
+               case 4:
+                       device->dev[i].speed = 100;
+                       break;
+               case 7:
                        if (++p == q)
                                return -EINVAL;
-               break;
-       default:
-               return -EINVAL;
-       }
+                       device->dev[i].speed = SPEED_CVT(*p);
+                       while (*p & 0x80)
+                               if (++p == q)
+                                       return -EINVAL;
+                       break;
+               default:
+                       return -EINVAL;
+               }
 
-       if (++p == q)
-               return -EINVAL;
-       if (*p == 0xff)
-               break;
-       scale = *p & 7;
-       if (scale == 7)
-               return -EINVAL;
-       device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
-       device->ndev++;
-       if (++p == q)
-               break;
-    }
+               if (++p == q)
+                       return -EINVAL;
+               if (*p == 0xff)
+                       break;
+               scale = *p & 7;
+               if (scale == 7)
+                       return -EINVAL;
+               device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
+               device->ndev++;
+               if (++p == q)
+                       break;
+       }
 
-    return 0;
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
 {
-    u_char *p;
-    if (tuple->TupleDataLen < 5)
-       return -EINVAL;
-    p = (u_char *) tuple->TupleData;
-    csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
-    csum->len = get_unaligned_le16(p + 2);
-    csum->sum = *(p + 4);
-    return 0;
+       u_char *p;
+       if (tuple->TupleDataLen < 5)
+               return -EINVAL;
+       p = (u_char *) tuple->TupleData;
+       csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
+       csum->len = get_unaligned_le16(p + 2);
+       csum->sum = *(p + 4);
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
 {
-    if (tuple->TupleDataLen < 4)
-       return -EINVAL;
-    link->addr = get_unaligned_le32(tuple->TupleData);
-    return 0;
+       if (tuple->TupleDataLen < 4)
+               return -EINVAL;
+       link->addr = get_unaligned_le32(tuple->TupleData);
+       return 0;
 }
 
-/*====================================================================*/
 
-static int parse_longlink_mfc(tuple_t *tuple,
-                             cistpl_longlink_mfc_t *link)
+static int parse_longlink_mfc(tuple_t *tuple, cistpl_longlink_mfc_t *link)
 {
-    u_char *p;
-    int i;
-
-    p = (u_char *)tuple->TupleData;
-
-    link->nfn = *p; p++;
-    if (tuple->TupleDataLen <= link->nfn*5)
-       return -EINVAL;
-    for (i = 0; i < link->nfn; i++) {
-       link->fn[i].space = *p; p++;
-       link->fn[i].addr = get_unaligned_le32(p);
-       p += 4;
-    }
-    return 0;
+       u_char *p;
+       int i;
+
+       p = (u_char *)tuple->TupleData;
+
+       link->nfn = *p; p++;
+       if (tuple->TupleDataLen <= link->nfn*5)
+               return -EINVAL;
+       for (i = 0; i < link->nfn; i++) {
+               link->fn[i].space = *p; p++;
+               link->fn[i].addr = get_unaligned_le32(p);
+               p += 4;
+       }
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_strings(u_char *p, u_char *q, int max,
                         char *s, u_char *ofs, u_char *found)
 {
-    int i, j, ns;
+       int i, j, ns;
 
-    if (p == q)
-           return -EINVAL;
-    ns = 0; j = 0;
-    for (i = 0; i < max; i++) {
-       if (*p == 0xff)
-               break;
-       ofs[i] = j;
-       ns++;
-       for (;;) {
-           s[j++] = (*p == 0xff) ? '\0' : *p;
-           if ((*p == '\0') || (*p == 0xff))
-                   break;
-           if (++p == q)
-                   return -EINVAL;
+       if (p == q)
+               return -EINVAL;
+       ns = 0; j = 0;
+       for (i = 0; i < max; i++) {
+               if (*p == 0xff)
+                       break;
+               ofs[i] = j;
+               ns++;
+               for (;;) {
+                       s[j++] = (*p == 0xff) ? '\0' : *p;
+                       if ((*p == '\0') || (*p == 0xff))
+                               break;
+                       if (++p == q)
+                               return -EINVAL;
+               }
+               if ((*p == 0xff) || (++p == q))
+                       break;
        }
-       if ((*p == 0xff) || (++p == q))
-               break;
-    }
-    if (found) {
-       *found = ns;
-       return 0;
-    } else {
+       if (found) {
+               *found = ns;
+               return 0;
+       }
+
        return (ns == max) ? 0 : -EINVAL;
-    }
 }
 
-/*====================================================================*/
 
 static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
 {
-    u_char *p, *q;
+       u_char *p, *q;
 
-    p = (u_char *)tuple->TupleData;
-    q = p + tuple->TupleDataLen;
+       p = (u_char *)tuple->TupleData;
+       q = p + tuple->TupleDataLen;
 
-    vers_1->major = *p; p++;
-    vers_1->minor = *p; p++;
-    if (p >= q)
-           return -EINVAL;
+       vers_1->major = *p; p++;
+       vers_1->minor = *p; p++;
+       if (p >= q)
+               return -EINVAL;
 
-    return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
-                        vers_1->str, vers_1->ofs, &vers_1->ns);
+       return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
+                       vers_1->str, vers_1->ofs, &vers_1->ns);
 }
 
-/*====================================================================*/
 
 static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
 {
-    u_char *p, *q;
+       u_char *p, *q;
 
-    p = (u_char *)tuple->TupleData;
-    q = p + tuple->TupleDataLen;
+       p = (u_char *)tuple->TupleData;
+       q = p + tuple->TupleDataLen;
 
-    return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
-                        altstr->str, altstr->ofs, &altstr->ns);
+       return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
+                       altstr->str, altstr->ofs, &altstr->ns);
 }
 
-/*====================================================================*/
 
 static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
 {
-    u_char *p, *q;
-    int nid;
+       u_char *p, *q;
+       int nid;
 
-    p = (u_char *)tuple->TupleData;
-    q = p + tuple->TupleDataLen;
+       p = (u_char *)tuple->TupleData;
+       q = p + tuple->TupleDataLen;
 
-    for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
-       if (p > q-2)
-               break;
-       jedec->id[nid].mfr = p[0];
-       jedec->id[nid].info = p[1];
-       p += 2;
-    }
-    jedec->nid = nid;
-    return 0;
+       for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
+               if (p > q-2)
+                       break;
+               jedec->id[nid].mfr = p[0];
+               jedec->id[nid].info = p[1];
+               p += 2;
+       }
+       jedec->nid = nid;
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
 {
-    if (tuple->TupleDataLen < 4)
-       return -EINVAL;
-    m->manf = get_unaligned_le16(tuple->TupleData);
-    m->card = get_unaligned_le16(tuple->TupleData + 2);
-    return 0;
+       if (tuple->TupleDataLen < 4)
+               return -EINVAL;
+       m->manf = get_unaligned_le16(tuple->TupleData);
+       m->card = get_unaligned_le16(tuple->TupleData + 2);
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
 {
-    u_char *p;
-    if (tuple->TupleDataLen < 2)
-       return -EINVAL;
-    p = (u_char *)tuple->TupleData;
-    f->func = p[0];
-    f->sysinit = p[1];
-    return 0;
+       u_char *p;
+       if (tuple->TupleDataLen < 2)
+               return -EINVAL;
+       p = (u_char *)tuple->TupleData;
+       f->func = p[0];
+       f->sysinit = p[1];
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
 {
-    u_char *p;
-    int i;
-    if (tuple->TupleDataLen < 1)
-       return -EINVAL;
-    p = (u_char *)tuple->TupleData;
-    f->type = p[0];
-    for (i = 1; i < tuple->TupleDataLen; i++)
-       f->data[i-1] = p[i];
-    return 0;
+       u_char *p;
+       int i;
+       if (tuple->TupleDataLen < 1)
+               return -EINVAL;
+       p = (u_char *)tuple->TupleData;
+       f->type = p[0];
+       for (i = 1; i < tuple->TupleDataLen; i++)
+               f->data[i-1] = p[i];
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_config(tuple_t *tuple, cistpl_config_t *config)
 {
-    int rasz, rmsz, i;
-    u_char *p;
-
-    p = (u_char *)tuple->TupleData;
-    rasz = *p & 0x03;
-    rmsz = (*p & 0x3c) >> 2;
-    if (tuple->TupleDataLen < rasz+rmsz+4)
-       return -EINVAL;
-    config->last_idx = *(++p);
-    p++;
-    config->base = 0;
-    for (i = 0; i <= rasz; i++)
-       config->base += p[i] << (8*i);
-    p += rasz+1;
-    for (i = 0; i < 4; i++)
-       config->rmask[i] = 0;
-    for (i = 0; i <= rmsz; i++)
-       config->rmask[i>>2] += p[i] << (8*(i%4));
-    config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
-    return 0;
+       int rasz, rmsz, i;
+       u_char *p;
+
+       p = (u_char *)tuple->TupleData;
+       rasz = *p & 0x03;
+       rmsz = (*p & 0x3c) >> 2;
+       if (tuple->TupleDataLen < rasz+rmsz+4)
+               return -EINVAL;
+       config->last_idx = *(++p);
+       p++;
+       config->base = 0;
+       for (i = 0; i <= rasz; i++)
+               config->base += p[i] << (8*i);
+       p += rasz+1;
+       for (i = 0; i < 4; i++)
+               config->rmask[i] = 0;
+       for (i = 0; i <= rmsz; i++)
+               config->rmask[i>>2] += p[i] << (8*(i%4));
+       config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
+       return 0;
 }
 
-/*======================================================================
+/* The following routines are all used to parse the nightmarish
+ * config table entries.
+ */
+
+static u_char *parse_power(u_char *p, u_char *q, cistpl_power_t *pwr)
+{
+       int i;
+       u_int scale;
 
-    The following routines are all used to parse the nightmarish
-    config table entries.
+       if (p == q)
+               return NULL;
+       pwr->present = *p;
+       pwr->flags = 0;
+       p++;
+       for (i = 0; i < 7; i++)
+               if (pwr->present & (1<<i)) {
+                       if (p == q)
+                               return NULL;
+                       pwr->param[i] = POWER_CVT(*p);
+                       scale = POWER_SCALE(*p);
+                       while (*p & 0x80) {
+                               if (++p == q)
+                                       return NULL;
+                               if ((*p & 0x7f) < 100)
+                                       pwr->param[i] +=
+                                               (*p & 0x7f) * scale / 100;
+                               else if (*p == 0x7d)
+                                       pwr->flags |= CISTPL_POWER_HIGHZ_OK;
+                               else if (*p == 0x7e)
+                                       pwr->param[i] = 0;
+                               else if (*p == 0x7f)
+                                       pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
+                               else
+                                       return NULL;
+                       }
+                       p++;
+               }
+       return p;
+}
 
-======================================================================*/
 
-static u_char *parse_power(u_char *p, u_char *q,
-                          cistpl_power_t *pwr)
+static u_char *parse_timing(u_char *p, u_char *q, cistpl_timing_t *timing)
 {
-    int i;
-    u_int scale;
-
-    if (p == q)
-           return NULL;
-    pwr->present = *p;
-    pwr->flags = 0;
-    p++;
-    for (i = 0; i < 7; i++)
-       if (pwr->present & (1<<i)) {
-           if (p == q)
-                   return NULL;
-           pwr->param[i] = POWER_CVT(*p);
-           scale = POWER_SCALE(*p);
-           while (*p & 0x80) {
+       u_char scale;
+
+       if (p == q)
+               return NULL;
+       scale = *p;
+       if ((scale & 3) != 3) {
                if (++p == q)
                        return NULL;
-               if ((*p & 0x7f) < 100)
-                   pwr->param[i] += (*p & 0x7f) * scale / 100;
-               else if (*p == 0x7d)
-                   pwr->flags |= CISTPL_POWER_HIGHZ_OK;
-               else if (*p == 0x7e)
-                   pwr->param[i] = 0;
-               else if (*p == 0x7f)
-                   pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
-               else
-                   return NULL;
-           }
-           p++;
-       }
-    return p;
+               timing->wait = SPEED_CVT(*p);
+               timing->waitscale = exponent[scale & 3];
+       } else
+               timing->wait = 0;
+       scale >>= 2;
+       if ((scale & 7) != 7) {
+               if (++p == q)
+                       return NULL;
+               timing->ready = SPEED_CVT(*p);
+               timing->rdyscale = exponent[scale & 7];
+       } else
+               timing->ready = 0;
+       scale >>= 3;
+       if (scale != 7) {
+               if (++p == q)
+                       return NULL;
+               timing->reserved = SPEED_CVT(*p);
+               timing->rsvscale = exponent[scale];
+       } else
+               timing->reserved = 0;
+       p++;
+       return p;
 }
 
-/*====================================================================*/
 
-static u_char *parse_timing(u_char *p, u_char *q,
-                           cistpl_timing_t *timing)
+static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
 {
-    u_char scale;
+       int i, j, bsz, lsz;
 
-    if (p == q)
-           return NULL;
-    scale = *p;
-    if ((scale & 3) != 3) {
-       if (++p == q)
-               return NULL;
-       timing->wait = SPEED_CVT(*p);
-       timing->waitscale = exponent[scale & 3];
-    } else
-       timing->wait = 0;
-    scale >>= 2;
-    if ((scale & 7) != 7) {
-       if (++p == q)
+       if (p == q)
                return NULL;
-       timing->ready = SPEED_CVT(*p);
-       timing->rdyscale = exponent[scale & 7];
-    } else
-       timing->ready = 0;
-    scale >>= 3;
-    if (scale != 7) {
+       io->flags = *p;
+
+       if (!(*p & 0x80)) {
+               io->nwin = 1;
+               io->win[0].base = 0;
+               io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
+               return p+1;
+       }
+
        if (++p == q)
                return NULL;
-       timing->reserved = SPEED_CVT(*p);
-       timing->rsvscale = exponent[scale];
-    } else
-       timing->reserved = 0;
-    p++;
-    return p;
-}
-
-/*====================================================================*/
+       io->nwin = (*p & 0x0f) + 1;
+       bsz = (*p & 0x30) >> 4;
+       if (bsz == 3)
+               bsz++;
+       lsz = (*p & 0xc0) >> 6;
+       if (lsz == 3)
+               lsz++;
+       p++;
 
-static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
-{
-    int i, j, bsz, lsz;
-
-    if (p == q)
-           return NULL;
-    io->flags = *p;
-
-    if (!(*p & 0x80)) {
-       io->nwin = 1;
-       io->win[0].base = 0;
-       io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
-       return p+1;
-    }
-
-    if (++p == q)
-           return NULL;
-    io->nwin = (*p & 0x0f) + 1;
-    bsz = (*p & 0x30) >> 4;
-    if (bsz == 3)
-           bsz++;
-    lsz = (*p & 0xc0) >> 6;
-    if (lsz == 3)
-           lsz++;
-    p++;
-
-    for (i = 0; i < io->nwin; i++) {
-       io->win[i].base = 0;
-       io->win[i].len = 1;
-       for (j = 0; j < bsz; j++, p++) {
-           if (p == q)
-                   return NULL;
-           io->win[i].base += *p << (j*8);
-       }
-       for (j = 0; j < lsz; j++, p++) {
-           if (p == q)
-                   return NULL;
-           io->win[i].len += *p << (j*8);
+       for (i = 0; i < io->nwin; i++) {
+               io->win[i].base = 0;
+               io->win[i].len = 1;
+               for (j = 0; j < bsz; j++, p++) {
+                       if (p == q)
+                               return NULL;
+                       io->win[i].base += *p << (j*8);
+               }
+               for (j = 0; j < lsz; j++, p++) {
+                       if (p == q)
+                               return NULL;
+                       io->win[i].len += *p << (j*8);
+               }
        }
-    }
-    return p;
+       return p;
 }
 
-/*====================================================================*/
 
 static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
 {
-    int i, j, asz, lsz, has_ha;
-    u_int len, ca, ha;
-
-    if (p == q)
-           return NULL;
-
-    mem->nwin = (*p & 0x07) + 1;
-    lsz = (*p & 0x18) >> 3;
-    asz = (*p & 0x60) >> 5;
-    has_ha = (*p & 0x80);
-    if (++p == q)
-           return NULL;
-
-    for (i = 0; i < mem->nwin; i++) {
-       len = ca = ha = 0;
-       for (j = 0; j < lsz; j++, p++) {
-           if (p == q)
-                   return NULL;
-           len += *p << (j*8);
-       }
-       for (j = 0; j < asz; j++, p++) {
-           if (p == q)
-                   return NULL;
-           ca += *p << (j*8);
+       int i, j, asz, lsz, has_ha;
+       u_int len, ca, ha;
+
+       if (p == q)
+               return NULL;
+
+       mem->nwin = (*p & 0x07) + 1;
+       lsz = (*p & 0x18) >> 3;
+       asz = (*p & 0x60) >> 5;
+       has_ha = (*p & 0x80);
+       if (++p == q)
+               return NULL;
+
+       for (i = 0; i < mem->nwin; i++) {
+               len = ca = ha = 0;
+               for (j = 0; j < lsz; j++, p++) {
+                       if (p == q)
+                               return NULL;
+                       len += *p << (j*8);
+               }
+               for (j = 0; j < asz; j++, p++) {
+                       if (p == q)
+                               return NULL;
+                       ca += *p << (j*8);
+               }
+               if (has_ha)
+                       for (j = 0; j < asz; j++, p++) {
+                               if (p == q)
+                                       return NULL;
+                               ha += *p << (j*8);
+                       }
+               mem->win[i].len = len << 8;
+               mem->win[i].card_addr = ca << 8;
+               mem->win[i].host_addr = ha << 8;
        }
-       if (has_ha)
-           for (j = 0; j < asz; j++, p++) {
-               if (p == q)
-                       return NULL;
-               ha += *p << (j*8);
-           }
-       mem->win[i].len = len << 8;
-       mem->win[i].card_addr = ca << 8;
-       mem->win[i].host_addr = ha << 8;
-    }
-    return p;
+       return p;
 }
 
-/*====================================================================*/
 
 static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
 {
-    if (p == q)
-           return NULL;
-    irq->IRQInfo1 = *p; p++;
-    if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
-       if (p+2 > q)
+       if (p == q)
                return NULL;
-       irq->IRQInfo2 = (p[1]<<8) + p[0];
-       p += 2;
-    }
-    return p;
+       irq->IRQInfo1 = *p; p++;
+       if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
+               if (p+2 > q)
+                       return NULL;
+               irq->IRQInfo2 = (p[1]<<8) + p[0];
+               p += 2;
+       }
+       return p;
 }
 
-/*====================================================================*/
 
 static int parse_cftable_entry(tuple_t *tuple,
                               cistpl_cftable_entry_t *entry)
 {
-    u_char *p, *q, features;
-
-    p = tuple->TupleData;
-    q = p + tuple->TupleDataLen;
-    entry->index = *p & 0x3f;
-    entry->flags = 0;
-    if (*p & 0x40)
-       entry->flags |= CISTPL_CFTABLE_DEFAULT;
-    if (*p & 0x80) {
-       if (++p == q)
-               return -EINVAL;
-       if (*p & 0x10)
-           entry->flags |= CISTPL_CFTABLE_BVDS;
-       if (*p & 0x20)
-           entry->flags |= CISTPL_CFTABLE_WP;
+       u_char *p, *q, features;
+
+       p = tuple->TupleData;
+       q = p + tuple->TupleDataLen;
+       entry->index = *p & 0x3f;
+       entry->flags = 0;
        if (*p & 0x40)
-           entry->flags |= CISTPL_CFTABLE_RDYBSY;
-       if (*p & 0x80)
-           entry->flags |= CISTPL_CFTABLE_MWAIT;
-       entry->interface = *p & 0x0f;
-    } else
-       entry->interface = 0;
-
-    /* Process optional features */
-    if (++p == q)
-           return -EINVAL;
-    features = *p; p++;
-
-    /* Power options */
-    if ((features & 3) > 0) {
-       p = parse_power(p, q, &entry->vcc);
-       if (p == NULL)
-               return -EINVAL;
-    } else
-       entry->vcc.present = 0;
-    if ((features & 3) > 1) {
-       p = parse_power(p, q, &entry->vpp1);
-       if (p == NULL)
-               return -EINVAL;
-    } else
-       entry->vpp1.present = 0;
-    if ((features & 3) > 2) {
-       p = parse_power(p, q, &entry->vpp2);
-       if (p == NULL)
-               return -EINVAL;
-    } else
-       entry->vpp2.present = 0;
+               entry->flags |= CISTPL_CFTABLE_DEFAULT;
+       if (*p & 0x80) {
+               if (++p == q)
+                       return -EINVAL;
+               if (*p & 0x10)
+                       entry->flags |= CISTPL_CFTABLE_BVDS;
+               if (*p & 0x20)
+                       entry->flags |= CISTPL_CFTABLE_WP;
+               if (*p & 0x40)
+                       entry->flags |= CISTPL_CFTABLE_RDYBSY;
+               if (*p & 0x80)
+                       entry->flags |= CISTPL_CFTABLE_MWAIT;
+               entry->interface = *p & 0x0f;
+       } else
+               entry->interface = 0;
 
-    /* Timing options */
-    if (features & 0x04) {
-       p = parse_timing(p, q, &entry->timing);
-       if (p == NULL)
-               return -EINVAL;
-    } else {
-       entry->timing.wait = 0;
-       entry->timing.ready = 0;
-       entry->timing.reserved = 0;
-    }
-
-    /* I/O window options */
-    if (features & 0x08) {
-       p = parse_io(p, q, &entry->io);
-       if (p == NULL)
+       /* Process optional features */
+       if (++p == q)
                return -EINVAL;
-    } else
-       entry->io.nwin = 0;
+       features = *p; p++;
 
-    /* Interrupt options */
-    if (features & 0x10) {
-       p = parse_irq(p, q, &entry->irq);
-       if (p == NULL)
-               return -EINVAL;
-    } else
-       entry->irq.IRQInfo1 = 0;
-
-    switch (features & 0x60) {
-    case 0x00:
-       entry->mem.nwin = 0;
-       break;
-    case 0x20:
-       entry->mem.nwin = 1;
-       entry->mem.win[0].len = get_unaligned_le16(p) << 8;
-       entry->mem.win[0].card_addr = 0;
-       entry->mem.win[0].host_addr = 0;
-       p += 2;
-       if (p > q)
-               return -EINVAL;
-       break;
-    case 0x40:
-       entry->mem.nwin = 1;
-       entry->mem.win[0].len = get_unaligned_le16(p) << 8;
-       entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
-       entry->mem.win[0].host_addr = 0;
-       p += 4;
-       if (p > q)
-               return -EINVAL;
-       break;
-    case 0x60:
-       p = parse_mem(p, q, &entry->mem);
-       if (p == NULL)
-               return -EINVAL;
-       break;
-    }
+       /* Power options */
+       if ((features & 3) > 0) {
+               p = parse_power(p, q, &entry->vcc);
+               if (p == NULL)
+                       return -EINVAL;
+       } else
+               entry->vcc.present = 0;
+       if ((features & 3) > 1) {
+               p = parse_power(p, q, &entry->vpp1);
+               if (p == NULL)
+                       return -EINVAL;
+       } else
+               entry->vpp1.present = 0;
+       if ((features & 3) > 2) {
+               p = parse_power(p, q, &entry->vpp2);
+               if (p == NULL)
+                       return -EINVAL;
+       } else
+               entry->vpp2.present = 0;
 
-    /* Misc features */
-    if (features & 0x80) {
-       if (p == q)
-               return -EINVAL;
-       entry->flags |= (*p << 8);
-       while (*p & 0x80)
-           if (++p == q)
-                   return -EINVAL;
-       p++;
-    }
+       /* Timing options */
+       if (features & 0x04) {
+               p = parse_timing(p, q, &entry->timing);
+               if (p == NULL)
+                       return -EINVAL;
+       } else {
+               entry->timing.wait = 0;
+               entry->timing.ready = 0;
+               entry->timing.reserved = 0;
+       }
 
-    entry->subtuples = q-p;
+       /* I/O window options */
+       if (features & 0x08) {
+               p = parse_io(p, q, &entry->io);
+               if (p == NULL)
+                       return -EINVAL;
+       } else
+               entry->io.nwin = 0;
+
+       /* Interrupt options */
+       if (features & 0x10) {
+               p = parse_irq(p, q, &entry->irq);
+               if (p == NULL)
+                       return -EINVAL;
+       } else
+               entry->irq.IRQInfo1 = 0;
+
+       switch (features & 0x60) {
+       case 0x00:
+               entry->mem.nwin = 0;
+               break;
+       case 0x20:
+               entry->mem.nwin = 1;
+               entry->mem.win[0].len = get_unaligned_le16(p) << 8;
+               entry->mem.win[0].card_addr = 0;
+               entry->mem.win[0].host_addr = 0;
+               p += 2;
+               if (p > q)
+                       return -EINVAL;
+               break;
+       case 0x40:
+               entry->mem.nwin = 1;
+               entry->mem.win[0].len = get_unaligned_le16(p) << 8;
+               entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
+               entry->mem.win[0].host_addr = 0;
+               p += 4;
+               if (p > q)
+                       return -EINVAL;
+               break;
+       case 0x60:
+               p = parse_mem(p, q, &entry->mem);
+               if (p == NULL)
+                       return -EINVAL;
+               break;
+       }
+
+       /* Misc features */
+       if (features & 0x80) {
+               if (p == q)
+                       return -EINVAL;
+               entry->flags |= (*p << 8);
+               while (*p & 0x80)
+                       if (++p == q)
+                               return -EINVAL;
+               p++;
+       }
+
+       entry->subtuples = q-p;
 
-    return 0;
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
 {
-    u_char *p, *q;
-    int n;
+       u_char *p, *q;
+       int n;
 
-    p = (u_char *)tuple->TupleData;
-    q = p + tuple->TupleDataLen;
+       p = (u_char *)tuple->TupleData;
+       q = p + tuple->TupleDataLen;
 
-    for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
-       if (p > q-6)
-               break;
-       geo->geo[n].buswidth = p[0];
-       geo->geo[n].erase_block = 1 << (p[1]-1);
-       geo->geo[n].read_block  = 1 << (p[2]-1);
-       geo->geo[n].write_block = 1 << (p[3]-1);
-       geo->geo[n].partition   = 1 << (p[4]-1);
-       geo->geo[n].interleave  = 1 << (p[5]-1);
-       p += 6;
-    }
-    geo->ngeo = n;
-    return 0;
+       for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
+               if (p > q-6)
+                       break;
+               geo->geo[n].buswidth = p[0];
+               geo->geo[n].erase_block = 1 << (p[1]-1);
+               geo->geo[n].read_block  = 1 << (p[2]-1);
+               geo->geo[n].write_block = 1 << (p[3]-1);
+               geo->geo[n].partition   = 1 << (p[4]-1);
+               geo->geo[n].interleave  = 1 << (p[5]-1);
+               p += 6;
+       }
+       geo->ngeo = n;
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
 {
-    u_char *p, *q;
-
-    if (tuple->TupleDataLen < 10)
-       return -EINVAL;
-
-    p = tuple->TupleData;
-    q = p + tuple->TupleDataLen;
-
-    v2->vers = p[0];
-    v2->comply = p[1];
-    v2->dindex = get_unaligned_le16(p + 2);
-    v2->vspec8 = p[6];
-    v2->vspec9 = p[7];
-    v2->nhdr = p[8];
-    p += 9;
-    return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
+       u_char *p, *q;
+
+       if (tuple->TupleDataLen < 10)
+               return -EINVAL;
+
+       p = tuple->TupleData;
+       q = p + tuple->TupleDataLen;
+
+       v2->vers = p[0];
+       v2->comply = p[1];
+       v2->dindex = get_unaligned_le16(p + 2);
+       v2->vspec8 = p[6];
+       v2->vspec9 = p[7];
+       v2->nhdr = p[8];
+       p += 9;
+       return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
 }
 
-/*====================================================================*/
 
 static int parse_org(tuple_t *tuple, cistpl_org_t *org)
 {
-    u_char *p, *q;
-    int i;
-
-    p = tuple->TupleData;
-    q = p + tuple->TupleDataLen;
-    if (p == q)
-           return -EINVAL;
-    org->data_org = *p;
-    if (++p == q)
-           return -EINVAL;
-    for (i = 0; i < 30; i++) {
-       org->desc[i] = *p;
-       if (*p == '\0')
-               break;
+       u_char *p, *q;
+       int i;
+
+       p = tuple->TupleData;
+       q = p + tuple->TupleDataLen;
+       if (p == q)
+               return -EINVAL;
+       org->data_org = *p;
        if (++p == q)
                return -EINVAL;
-    }
-    return 0;
+       for (i = 0; i < 30; i++) {
+               org->desc[i] = *p;
+               if (*p == '\0')
+                       break;
+               if (++p == q)
+                       return -EINVAL;
+       }
+       return 0;
 }
 
-/*====================================================================*/
 
 static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
 {
-    u_char *p;
+       u_char *p;
 
-    if (tuple->TupleDataLen < 10)
-       return -EINVAL;
+       if (tuple->TupleDataLen < 10)
+               return -EINVAL;
 
-    p = tuple->TupleData;
+       p = tuple->TupleData;
 
-    fmt->type = p[0];
-    fmt->edc = p[1];
-    fmt->offset = get_unaligned_le32(p + 2);
-    fmt->length = get_unaligned_le32(p + 6);
+       fmt->type = p[0];
+       fmt->edc = p[1];
+       fmt->offset = get_unaligned_le32(p + 2);
+       fmt->length = get_unaligned_le32(p + 6);
 
-    return 0;
+       return 0;
 }
 
-/*====================================================================*/
 
 int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
 {
-    int ret = 0;
-
-    if (tuple->TupleDataLen > tuple->TupleDataMax)
-       return -EINVAL;
-    switch (tuple->TupleCode) {
-    case CISTPL_DEVICE:
-    case CISTPL_DEVICE_A:
-       ret = parse_device(tuple, &parse->device);
-       break;
-    case CISTPL_CHECKSUM:
-       ret = parse_checksum(tuple, &parse->checksum);
-       break;
-    case CISTPL_LONGLINK_A:
-    case CISTPL_LONGLINK_C:
-       ret = parse_longlink(tuple, &parse->longlink);
-       break;
-    case CISTPL_LONGLINK_MFC:
-       ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
-       break;
-    case CISTPL_VERS_1:
-       ret = parse_vers_1(tuple, &parse->version_1);
-       break;
-    case CISTPL_ALTSTR:
-       ret = parse_altstr(tuple, &parse->altstr);
-       break;
-    case CISTPL_JEDEC_A:
-    case CISTPL_JEDEC_C:
-       ret = parse_jedec(tuple, &parse->jedec);
-       break;
-    case CISTPL_MANFID:
-       ret = parse_manfid(tuple, &parse->manfid);
-       break;
-    case CISTPL_FUNCID:
-       ret = parse_funcid(tuple, &parse->funcid);
-       break;
-    case CISTPL_FUNCE:
-       ret = parse_funce(tuple, &parse->funce);
-       break;
-    case CISTPL_CONFIG:
-       ret = parse_config(tuple, &parse->config);
-       break;
-    case CISTPL_CFTABLE_ENTRY:
-       ret = parse_cftable_entry(tuple, &parse->cftable_entry);
-       break;
-    case CISTPL_DEVICE_GEO:
-    case CISTPL_DEVICE_GEO_A:
-       ret = parse_device_geo(tuple, &parse->device_geo);
-       break;
-    case CISTPL_VERS_2:
-       ret = parse_vers_2(tuple, &parse->vers_2);
-       break;
-    case CISTPL_ORG:
-       ret = parse_org(tuple, &parse->org);
-       break;
-    case CISTPL_FORMAT:
-    case CISTPL_FORMAT_A:
-       ret = parse_format(tuple, &parse->format);
-       break;
-    case CISTPL_NO_LINK:
-    case CISTPL_LINKTARGET:
-       ret = 0;
-       break;
-    default:
-       ret = -EINVAL;
-       break;
-    }
-    if (ret)
-           pr_debug("parse_tuple failed %d\n", ret);
-    return ret;
+       int ret = 0;
+
+       if (tuple->TupleDataLen > tuple->TupleDataMax)
+               return -EINVAL;
+       switch (tuple->TupleCode) {
+       case CISTPL_DEVICE:
+       case CISTPL_DEVICE_A:
+               ret = parse_device(tuple, &parse->device);
+               break;
+       case CISTPL_CHECKSUM:
+               ret = parse_checksum(tuple, &parse->checksum);
+               break;
+       case CISTPL_LONGLINK_A:
+       case CISTPL_LONGLINK_C:
+               ret = parse_longlink(tuple, &parse->longlink);
+               break;
+       case CISTPL_LONGLINK_MFC:
+               ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
+               break;
+       case CISTPL_VERS_1:
+               ret = parse_vers_1(tuple, &parse->version_1);
+               break;
+       case CISTPL_ALTSTR:
+               ret = parse_altstr(tuple, &parse->altstr);
+               break;
+       case CISTPL_JEDEC_A:
+       case CISTPL_JEDEC_C:
+               ret = parse_jedec(tuple, &parse->jedec);
+               break;
+       case CISTPL_MANFID:
+               ret = parse_manfid(tuple, &parse->manfid);
+               break;
+       case CISTPL_FUNCID:
+               ret = parse_funcid(tuple, &parse->funcid);
+               break;
+       case CISTPL_FUNCE:
+               ret = parse_funce(tuple, &parse->funce);
+               break;
+       case CISTPL_CONFIG:
+               ret = parse_config(tuple, &parse->config);
+               break;
+       case CISTPL_CFTABLE_ENTRY:
+               ret = parse_cftable_entry(tuple, &parse->cftable_entry);
+               break;
+       case CISTPL_DEVICE_GEO:
+       case CISTPL_DEVICE_GEO_A:
+               ret = parse_device_geo(tuple, &parse->device_geo);
+               break;
+       case CISTPL_VERS_2:
+               ret = parse_vers_2(tuple, &parse->vers_2);
+               break;
+       case CISTPL_ORG:
+               ret = parse_org(tuple, &parse->org);
+               break;
+       case CISTPL_FORMAT:
+       case CISTPL_FORMAT_A:
+               ret = parse_format(tuple, &parse->format);
+               break;
+       case CISTPL_NO_LINK:
+       case CISTPL_LINKTARGET:
+               ret = 0;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       if (ret)
+               pr_debug("parse_tuple failed %d\n", ret);
+       return ret;
 }
 EXPORT_SYMBOL(pcmcia_parse_tuple);
 
-/*======================================================================
 
-    This is used internally by Card Services to look up CIS stuff.
-
-======================================================================*/
-
-int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse)
+/**
+ * pccard_read_tuple() - internal CIS tuple access
+ * @s:         the struct pcmcia_socket where the card is inserted
+ * @function:  the device function we loop for
+ * @code:      which CIS code shall we look for?
+ * @parse:     buffer where the tuple shall be parsed (or NULL, if no parse)
+ *
+ * pccard_read_tuple() reads out one tuple and attempts to parse it
+ */
+int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
+               cisdata_t code, void *parse)
 {
-    tuple_t tuple;
-    cisdata_t *buf;
-    int ret;
-
-    buf = kmalloc(256, GFP_KERNEL);
-    if (buf == NULL) {
-           dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
-           return -ENOMEM;
-    }
-    tuple.DesiredTuple = code;
-    tuple.Attributes = 0;
-    if (function == BIND_FN_ALL)
-           tuple.Attributes = TUPLE_RETURN_COMMON;
-    ret = pccard_get_first_tuple(s, function, &tuple);
-    if (ret != 0)
-           goto done;
-    tuple.TupleData = buf;
-    tuple.TupleOffset = 0;
-    tuple.TupleDataMax = 255;
-    ret = pccard_get_tuple_data(s, &tuple);
-    if (ret != 0)
-           goto done;
-    ret = pcmcia_parse_tuple(&tuple, parse);
+       tuple_t tuple;
+       cisdata_t *buf;
+       int ret;
+
+       buf = kmalloc(256, GFP_KERNEL);
+       if (buf == NULL) {
+               dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+               return -ENOMEM;
+       }
+       tuple.DesiredTuple = code;
+       tuple.Attributes = 0;
+       if (function == BIND_FN_ALL)
+               tuple.Attributes = TUPLE_RETURN_COMMON;
+       ret = pccard_get_first_tuple(s, function, &tuple);
+       if (ret != 0)
+               goto done;
+       tuple.TupleData = buf;
+       tuple.TupleOffset = 0;
+       tuple.TupleDataMax = 255;
+       ret = pccard_get_tuple_data(s, &tuple);
+       if (ret != 0)
+               goto done;
+       ret = pcmcia_parse_tuple(&tuple, parse);
 done:
-    kfree(buf);
-    return ret;
+       kfree(buf);
+       return ret;
 }
 
 
index 3889cf07d6ce34bd799741c12c35ff1998005466..9254ab0b29b1b1262fdc1fdb9b1c705041128ae7 100644 (file)
@@ -42,7 +42,6 @@ struct db1x_pcmcia_sock {
        int             nr;             /* socket number */
        void            *virt_io;
 
-       /* the "pseudo" addresses of the PCMCIA space. */
        phys_addr_t     phys_io;
        phys_addr_t     phys_attr;
        phys_addr_t     phys_mem;
@@ -437,7 +436,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev)
         * This includes IRQs for Carddetection/ejection, the card
         *  itself and optional status change detection.
         * Also, the memory areas covered by a socket.  For these
-        *  we require the 32bit "pseudo" addresses (see the au1000.h
+        *  we require the real 36bit addresses (see the au1000.h
         *  header for more information).
         */
 
@@ -459,11 +458,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev)
 
        ret = -ENODEV;
 
-       /*
-        * pseudo-attr:  The 32bit address of the PCMCIA attribute space
-        * for this socket (usually the 36bit address shifted 4 to the
-        * right).
-        */
+       /* 36bit PCMCIA Attribute area address */
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
        if (!r) {
                printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n",
@@ -472,10 +467,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev)
        }
        sock->phys_attr = r->start;
 
-       /*
-        * pseudo-mem:  The 32bit address of the PCMCIA memory space for
-        * this socket (usually the 36bit address shifted 4 to the right)
-        */
+       /* 36bit PCMCIA Memory area address */
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
        if (!r) {
                printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n",
@@ -484,10 +476,7 @@ static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev)
        }
        sock->phys_mem = r->start;
 
-       /*
-        * pseudo-io:  The 32bit address of the PCMCIA IO space for this
-        * socket (usually the 36bit address shifted 4 to the right).
-        */
+       /* 36bit PCMCIA IO area address */
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
        if (!r) {
                printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n",
index e1741cd875aaae9861bcab302e67be4c106af1e5..7c204910a77742980cf7cdbb448110f65f763611 100644 (file)
@@ -48,23 +48,13 @@ MODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>");
  *     Specifies the interrupt delivery mode.  The default (1) is to use PCI
  *     interrupts; a value of 0 selects ISA interrupts. This must be set for
  *     correct operation of PCI card readers.
- *
- *  irq_list=i,j,...
- *     This list limits the set of interrupts that can be used by PCMCIA
- *     cards.
- *     The default list is 3,4,5,7,9,10,11.
- *     (irq_list parameter is not used, if irq_mode = 1)
  */
 
 static int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */
-static int irq_list[16];
-static unsigned int irq_list_count = 0;
 
 module_param(irq_mode, int, 0444);
-module_param_array(irq_list, int, &irq_list_count, 0444);
 MODULE_PARM_DESC(irq_mode,
                "interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1");
-MODULE_PARM_DESC(irq_list, "interrupts that can be used by PCMCIA cards");
 
 static DEFINE_SPINLOCK(port_lock);
 
@@ -605,13 +595,7 @@ static u_int __devinit pd6729_isa_scan(void)
                return 0;
        }
 
-       if (irq_list_count == 0)
-               mask0 = 0xffff;
-       else
-               for (i = mask0 = 0; i < irq_list_count; i++)
-                       mask0 |= (1<<irq_list[i]);
-
-       mask0 &= PD67_MASK;
+       mask0 = PD67_MASK;
 
        /* just find interrupts that aren't in use */
        for (i = 0; i < 16; i++)
index e6f7d410aed618a8f1a80d8bfcdd4715b5cb97da..452c83b512c47eb8413ae55ffebf1a74251eca84 100644 (file)
@@ -79,9 +79,8 @@ static resource_size_t pcmcia_align(void *align_data,
 
 #ifdef CONFIG_X86
        if (res->flags & IORESOURCE_IO) {
-               if (start & 0x300) {
+               if (start & 0x300)
                        start = (start + 0x3ff) & ~0x3ff;
-               }
        }
 #endif
 
index 61560cd6e2877b5b37f1c804e291a8d722027503..f9009d34254b7c2a3edcc0fb1f3161d83eeb8c14 100644 (file)
@@ -218,11 +218,7 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev)
 
        ret = -ENODEV;
 
-       /*
-        * pseudo-attr:  The 32bit address of the PCMCIA attribute space
-        * for this socket (usually the 36bit address shifted 4 to the
-        * right).
-        */
+       /* 36bit PCMCIA Attribute area address */
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
        if (!r) {
                dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n");
@@ -230,10 +226,7 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev)
        }
        sock->phys_attr = r->start;
 
-       /*
-        * pseudo-mem:  The 32bit address of the PCMCIA memory space for
-        * this socket (usually the 36bit address shifted 4 to the right)
-        */
+       /* 36bit PCMCIA Memory area address */
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
        if (!r) {
                dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n");
@@ -241,10 +234,7 @@ static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev)
        }
        sock->phys_mem = r->start;
 
-       /*
-        * pseudo-io:  The 32bit address of the PCMCIA IO space for this
-        * socket (usually the 36bit address shifted 4 to the right).
-        */
+       /* 36bit PCMCIA IO area address */
        r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
        if (!r) {
                dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n");
index b85375f876228883e744d2c23ccc5b4c0667088f..967c766f53ba63a0c8939ce236862ae86971204c 100644 (file)
@@ -1408,10 +1408,10 @@ static struct pci_device_id yenta_table[] = {
        CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_7510, TI12XX),
        CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_7610, TI12XX),
 
-       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_710, TI12XX),
-       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_712, TI12XX),
-       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_720, TI12XX),
-       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_722, TI12XX),
+       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_710, ENE),
+       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_712, ENE),
+       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_720, ENE),
+       CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_722, ENE),
        CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1211, ENE),
        CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1225, ENE),
        CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1410, ENE),
index d4b3d67f05487e484a811a152ba6f233b8c00c4a..bf146721395478e8fe15337be20166131fa7ac6e 100644 (file)
@@ -98,10 +98,10 @@ config BATTERY_WM97XX
          Say Y to enable support for battery measured by WM97xx aux port.
 
 config BATTERY_BQ27x00
-       tristate "BQ27200 battery driver"
+       tristate "BQ27x00 battery driver"
        depends on I2C
        help
-         Say Y here to enable support for batteries with BQ27200(I2C) chip.
+         Say Y here to enable support for batteries with BQ27x00 (I2C) chips.
 
 config BATTERY_DA9030
        tristate "DA9030 battery driver"
index 62bb98124e26e0b273de974e9ee357ce875dcf87..bece33ed873cbc425accae82fb7ed7bdadb08615 100644 (file)
 #include <linux/i2c.h>
 #include <asm/unaligned.h>
 
-#define DRIVER_VERSION                 "1.0.0"
+#define DRIVER_VERSION                 "1.1.0"
 
 #define BQ27x00_REG_TEMP               0x06
 #define BQ27x00_REG_VOLT               0x08
-#define BQ27x00_REG_RSOC               0x0B /* Relative State-of-Charge */
 #define BQ27x00_REG_AI                 0x14
 #define BQ27x00_REG_FLAGS              0x0A
+#define BQ27x00_REG_TTE                        0x16
+#define BQ27x00_REG_TTF                        0x18
+#define BQ27x00_REG_TTECP              0x26
+
+#define BQ27000_REG_RSOC               0x0B /* Relative State-of-Charge */
+#define BQ27000_FLAG_CHGS              BIT(7)
+
+#define BQ27500_REG_SOC                        0x2c
+#define BQ27500_FLAG_DSC               BIT(0)
+#define BQ27500_FLAG_FC                        BIT(9)
 
 /* If the system has several batteries we need a different name for each
  * of them...
@@ -46,25 +55,28 @@ struct bq27x00_access_methods {
                struct bq27x00_device_info *di);
 };
 
+enum bq27x00_chip { BQ27000, BQ27500 };
+
 struct bq27x00_device_info {
        struct device           *dev;
        int                     id;
-       int                     voltage_uV;
-       int                     current_uA;
-       int                     temp_C;
-       int                     charge_rsoc;
        struct bq27x00_access_methods   *bus;
        struct power_supply     bat;
+       enum bq27x00_chip       chip;
 
        struct i2c_client       *client;
 };
 
 static enum power_supply_property bq27x00_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_PRESENT,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
 };
 
 /*
@@ -74,16 +86,11 @@ static enum power_supply_property bq27x00_battery_props[] = {
 static int bq27x00_read(u8 reg, int *rt_value, int b_single,
                        struct bq27x00_device_info *di)
 {
-       int ret;
-
-       ret = di->bus->read(reg, rt_value, b_single, di);
-       *rt_value = be16_to_cpu(*rt_value);
-
-       return ret;
+       return di->bus->read(reg, rt_value, b_single, di);
 }
 
 /*
- * Return the battery temperature in Celsius degrees
+ * Return the battery temperature in tenths of degree Celsius
  * Or < 0 if something fails.
  */
 static int bq27x00_battery_temperature(struct bq27x00_device_info *di)
@@ -97,7 +104,10 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di)
                return ret;
        }
 
-       return (temp >> 2) - 273;
+       if (di->chip == BQ27500)
+               return temp - 2731;
+       else
+               return ((temp >> 2) - 273) * 10;
 }
 
 /*
@@ -115,7 +125,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di)
                return ret;
        }
 
-       return volt;
+       return volt * 1000;
 }
 
 /*
@@ -134,16 +144,23 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di)
                dev_err(di->dev, "error reading current\n");
                return 0;
        }
-       ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
-       if (ret < 0) {
-               dev_err(di->dev, "error reading flags\n");
-               return 0;
-       }
-       if ((flags & (1 << 7)) != 0) {
-               dev_dbg(di->dev, "negative current!\n");
-               return -curr;
+
+       if (di->chip == BQ27500) {
+               /* bq27500 returns signed value */
+               curr = (int)(s16)curr;
+       } else {
+               ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
+               if (ret < 0) {
+                       dev_err(di->dev, "error reading flags\n");
+                       return 0;
+               }
+               if (flags & BQ27000_FLAG_CHGS) {
+                       dev_dbg(di->dev, "negative current!\n");
+                       curr = -curr;
+               }
        }
-       return curr;
+
+       return curr * 1000;
 }
 
 /*
@@ -155,13 +172,70 @@ static int bq27x00_battery_rsoc(struct bq27x00_device_info *di)
        int ret;
        int rsoc = 0;
 
-       ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di);
+       if (di->chip == BQ27500)
+               ret = bq27x00_read(BQ27500_REG_SOC, &rsoc, 0, di);
+       else
+               ret = bq27x00_read(BQ27000_REG_RSOC, &rsoc, 1, di);
        if (ret) {
                dev_err(di->dev, "error reading relative State-of-Charge\n");
                return ret;
        }
 
-       return rsoc >> 8;
+       return rsoc;
+}
+
+static int bq27x00_battery_status(struct bq27x00_device_info *di,
+                                 union power_supply_propval *val)
+{
+       int flags = 0;
+       int status;
+       int ret;
+
+       ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
+       if (ret < 0) {
+               dev_err(di->dev, "error reading flags\n");
+               return ret;
+       }
+
+       if (di->chip == BQ27500) {
+               if (flags & BQ27500_FLAG_FC)
+                       status = POWER_SUPPLY_STATUS_FULL;
+               else if (flags & BQ27500_FLAG_DSC)
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+       } else {
+               if (flags & BQ27000_FLAG_CHGS)
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       val->intval = status;
+       return 0;
+}
+
+/*
+ * Read a time register.
+ * Return < 0 if something fails.
+ */
+static int bq27x00_battery_time(struct bq27x00_device_info *di, int reg,
+                               union power_supply_propval *val)
+{
+       int tval = 0;
+       int ret;
+
+       ret = bq27x00_read(reg, &tval, 0, di);
+       if (ret) {
+               dev_err(di->dev, "error reading register %02x\n", reg);
+               return ret;
+       }
+
+       if (tval == 65535)
+               return -ENODATA;
+
+       val->intval = tval * 60;
+       return 0;
 }
 
 #define to_bq27x00_device_info(x) container_of((x), \
@@ -171,9 +245,13 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
                                        enum power_supply_property psp,
                                        union power_supply_propval *val)
 {
+       int ret = 0;
        struct bq27x00_device_info *di = to_bq27x00_device_info(psy);
 
        switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = bq27x00_battery_status(di, val);
+               break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = bq27x00_battery_voltage(di);
@@ -189,11 +267,20 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_TEMP:
                val->intval = bq27x00_battery_temperature(di);
                break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+               ret = bq27x00_battery_time(di, BQ27x00_REG_TTE, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+               ret = bq27x00_battery_time(di, BQ27x00_REG_TTECP, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+               ret = bq27x00_battery_time(di, BQ27x00_REG_TTF, val);
+               break;
        default:
                return -EINVAL;
        }
 
-       return 0;
+       return ret;
 }
 
 static void bq27x00_powersupply_init(struct bq27x00_device_info *di)
@@ -206,10 +293,10 @@ static void bq27x00_powersupply_init(struct bq27x00_device_info *di)
 }
 
 /*
- * BQ27200 specific code
+ * i2c specific code
  */
 
-static int bq27200_read(u8 reg, int *rt_value, int b_single,
+static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single,
                        struct bq27x00_device_info *di)
 {
        struct i2c_client *client = di->client;
@@ -238,7 +325,7 @@ static int bq27200_read(u8 reg, int *rt_value, int b_single,
                err = i2c_transfer(client->adapter, msg, 1);
                if (err >= 0) {
                        if (!b_single)
-                               *rt_value = get_unaligned_be16(data);
+                               *rt_value = get_unaligned_le16(data);
                        else
                                *rt_value = data[0];
 
@@ -248,7 +335,7 @@ static int bq27200_read(u8 reg, int *rt_value, int b_single,
        return err;
 }
 
-static int bq27200_battery_probe(struct i2c_client *client,
+static int bq27x00_battery_probe(struct i2c_client *client,
                                 const struct i2c_device_id *id)
 {
        char *name;
@@ -267,7 +354,7 @@ static int bq27200_battery_probe(struct i2c_client *client,
        if (retval < 0)
                return retval;
 
-       name = kasprintf(GFP_KERNEL, "bq27200-%d", num);
+       name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
        if (!name) {
                dev_err(&client->dev, "failed to allocate device name\n");
                retval = -ENOMEM;
@@ -281,6 +368,7 @@ static int bq27200_battery_probe(struct i2c_client *client,
                goto batt_failed_2;
        }
        di->id = num;
+       di->chip = id->driver_data;
 
        bus = kzalloc(sizeof(*bus), GFP_KERNEL);
        if (!bus) {
@@ -293,7 +381,7 @@ static int bq27200_battery_probe(struct i2c_client *client,
        i2c_set_clientdata(client, di);
        di->dev = &client->dev;
        di->bat.name = name;
-       bus->read = &bq27200_read;
+       bus->read = &bq27x00_read_i2c;
        di->bus = bus;
        di->client = client;
 
@@ -323,7 +411,7 @@ batt_failed_1:
        return retval;
 }
 
-static int bq27200_battery_remove(struct i2c_client *client)
+static int bq27x00_battery_remove(struct i2c_client *client)
 {
        struct bq27x00_device_info *di = i2c_get_clientdata(client);
 
@@ -344,27 +432,28 @@ static int bq27200_battery_remove(struct i2c_client *client)
  * Module stuff
  */
 
-static const struct i2c_device_id bq27200_id[] = {
-       { "bq27200", 0 },
+static const struct i2c_device_id bq27x00_id[] = {
+       { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
+       { "bq27500", BQ27500 },
        {},
 };
 
-static struct i2c_driver bq27200_battery_driver = {
+static struct i2c_driver bq27x00_battery_driver = {
        .driver = {
-               .name = "bq27200-battery",
+               .name = "bq27x00-battery",
        },
-       .probe = bq27200_battery_probe,
-       .remove = bq27200_battery_remove,
-       .id_table = bq27200_id,
+       .probe = bq27x00_battery_probe,
+       .remove = bq27x00_battery_remove,
+       .id_table = bq27x00_id,
 };
 
 static int __init bq27x00_battery_init(void)
 {
        int ret;
 
-       ret = i2c_add_driver(&bq27200_battery_driver);
+       ret = i2c_add_driver(&bq27x00_battery_driver);
        if (ret)
-               printk(KERN_ERR "Unable to register BQ27200 driver\n");
+               printk(KERN_ERR "Unable to register BQ27x00 driver\n");
 
        return ret;
 }
@@ -372,7 +461,7 @@ module_init(bq27x00_battery_init);
 
 static void __exit bq27x00_battery_exit(void)
 {
-       i2c_del_driver(&bq27200_battery_driver);
+       i2c_del_driver(&bq27x00_battery_driver);
 }
 module_exit(bq27x00_battery_exit);
 
index 3364198134a1d55a96789d1a7333a36432e14059..a2e71f7b27fb6d68a26029086e88e2b830744400 100644 (file)
@@ -509,7 +509,7 @@ static int da9030_battery_probe(struct platform_device *pdev)
 
        charger->master = pdev->dev.parent;
 
-       /* 10 seconds between monotor runs unless platfrom defines other
+       /* 10 seconds between monitor runs unless platform defines other
           interval */
        charger->interval = msecs_to_jiffies(
                (pdata->batmon_interval ? : 10) * 1000);
index 6ea3cb5837c707963784555aabafe14c35b0b48a..23eed356a854c880fc3d26854fe90f15105c0f21 100644 (file)
@@ -26,7 +26,7 @@
 
 static DEFINE_MUTEX(bat_lock);
 static struct work_struct bat_work;
-struct mutex work_lock;
+static struct mutex work_lock;
 static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
 static struct wm97xx_batt_info *gpdata;
 static enum power_supply_property *prop;
@@ -203,7 +203,7 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
                        goto err2;
                ret = request_irq(gpio_to_irq(pdata->charge_gpio),
                                wm97xx_chrg_irq, IRQF_DISABLED,
-                               "AC Detect", 0);
+                               "AC Detect", dev);
                if (ret)
                        goto err2;
                props++;        /* POWER_SUPPLY_PROP_STATUS */
index 262f62eec837438f36a5adbf53ebdabae96fe9f7..834b4844182937c9504fdad644c651c7e37edbb9 100644 (file)
@@ -27,6 +27,17 @@ config REGULATOR_DEBUG
        help
          Say yes here to enable debugging support.
 
+config REGULATOR_DUMMY
+       bool "Provide a dummy regulator if regulator lookups fail"
+       help
+         If this option is enabled then when a regulator lookup fails
+         and the board has not specified that it has provided full
+         constraints then the regulator core will provide an always
+         enabled dummy regulator will be provided, allowing consumer
+         drivers to continue.
+
+         A warning will be generated when this substitution is done.
+
 config REGULATOR_FIXED_VOLTAGE
        tristate "Fixed voltage regulator support"
        help
@@ -69,6 +80,13 @@ config REGULATOR_MAX1586
          regulator via I2C bus. The provided regulator is suitable
          for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
 
+config REGULATOR_MAX8649
+       tristate "Maxim 8649 voltage regulator"
+       depends on I2C
+       help
+         This driver controls a Maxim 8649 voltage output regulator via
+         I2C bus.
+
 config REGULATOR_MAX8660
        tristate "Maxim 8660/8661 voltage regulator"
        depends on I2C
@@ -91,19 +109,26 @@ config REGULATOR_WM831X
          of PMIC devices.
 
 config REGULATOR_WM8350
-       tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC"
+       tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC"
        depends on MFD_WM8350
        help
          This driver provides support for the voltage and current regulators
           of the WM8350 AudioPlus PMIC.
 
 config REGULATOR_WM8400
-       tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC"
+       tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC"
        depends on MFD_WM8400
        help
          This driver provides support for the voltage regulators of the
          WM8400 AudioPlus PMIC.
 
+config REGULATOR_WM8994
+       tristate "Wolfson Microelectronics WM8994 CODEC"
+       depends on MFD_WM8994
+       help
+         This driver provides support for the voltage regulators on the
+         WM8994 CODEC.
+
 config REGULATOR_DA903X
        tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC"
        depends on PMIC_DA903X
index b3c806c794157aac2f2604d16e9b6a47d82d0ac2..e845b66ad59c95315ec1bca3a94fa33f5cf0f207 100644 (file)
@@ -9,15 +9,18 @@ obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
 obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
 
 obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
+obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8649)        += max8649.o
 obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
 obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
 obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
+obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
 obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
index b349db4504b79c6d122ee94221793541af4e0f32..7de950959ed23e8892f5a1d55db4856dd87e518d 100644 (file)
@@ -561,7 +561,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
  * for all the different regulators.
  */
 
-static int __init ab3100_regulators_probe(struct platform_device *pdev)
+static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
 {
        struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
        struct ab3100 *ab3100 = platform_get_drvdata(pdev);
@@ -641,7 +641,7 @@ static int __init ab3100_regulators_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int __exit ab3100_regulators_remove(struct platform_device *pdev)
+static int __devexit ab3100_regulators_remove(struct platform_device *pdev)
 {
        int i;
 
@@ -659,7 +659,7 @@ static struct platform_driver ab3100_regulators_driver = {
                .owner = THIS_MODULE,
        },
        .probe = ab3100_regulators_probe,
-       .remove = __exit_p(ab3100_regulators_remove),
+       .remove = __devexit_p(ab3100_regulators_remove),
 };
 
 static __init int ab3100_regulators_init(void)
index b60a4c9f8f1680153e7c66c9c6fb5c72ec022dac..c7bbe30010f70b3dc69542eb99fe14488cd31f45 100644 (file)
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/suspend.h>
+#include <linux/delay.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 
+#include "dummy.h"
+
 #define REGULATOR_VERSION "0.5"
 
 static DEFINE_MUTEX(regulator_list_mutex);
@@ -1084,6 +1087,13 @@ overflow_err:
        return NULL;
 }
 
+static int _regulator_get_enable_time(struct regulator_dev *rdev)
+{
+       if (!rdev->desc->ops->enable_time)
+               return 0;
+       return rdev->desc->ops->enable_time(rdev);
+}
+
 /* Internal regulator request function */
 static struct regulator *_regulator_get(struct device *dev, const char *id,
                                        int exclusive)
@@ -1115,6 +1125,22 @@ static struct regulator *_regulator_get(struct device *dev, const char *id,
                        goto found;
                }
        }
+
+#ifdef CONFIG_REGULATOR_DUMMY
+       if (!devname)
+               devname = "deviceless";
+
+       /* If the board didn't flag that it was fully constrained then
+        * substitute in a dummy regulator so consumers can continue.
+        */
+       if (!has_full_constraints) {
+               pr_warning("%s supply %s not found, using dummy regulator\n",
+                          devname, id);
+               rdev = dummy_regulator_rdev;
+               goto found;
+       }
+#endif
+
        mutex_unlock(&regulator_list_mutex);
        return regulator;
 
@@ -1251,7 +1277,7 @@ static int _regulator_can_change_status(struct regulator_dev *rdev)
 /* locks held by regulator_enable() */
 static int _regulator_enable(struct regulator_dev *rdev)
 {
-       int ret;
+       int ret, delay;
 
        /* do we need to enable the supply regulator first */
        if (rdev->supply) {
@@ -1275,13 +1301,34 @@ static int _regulator_enable(struct regulator_dev *rdev)
                        if (!_regulator_can_change_status(rdev))
                                return -EPERM;
 
-                       if (rdev->desc->ops->enable) {
-                               ret = rdev->desc->ops->enable(rdev);
-                               if (ret < 0)
-                                       return ret;
-                       } else {
+                       if (!rdev->desc->ops->enable)
                                return -EINVAL;
+
+                       /* Query before enabling in case configuration
+                        * dependant.  */
+                       ret = _regulator_get_enable_time(rdev);
+                       if (ret >= 0) {
+                               delay = ret;
+                       } else {
+                               printk(KERN_WARNING
+                                       "%s: enable_time() failed for %s: %d\n",
+                                       __func__, rdev_get_name(rdev),
+                                       ret);
+                               delay = 0;
                        }
+
+                       /* Allow the regulator to ramp; it would be useful
+                        * to extend this for bulk operations so that the
+                        * regulators can ramp together.  */
+                       ret = rdev->desc->ops->enable(rdev);
+                       if (ret < 0)
+                               return ret;
+
+                       if (delay >= 1000)
+                               mdelay(delay / 1000);
+                       else if (delay)
+                               udelay(delay);
+
                } else if (ret < 0) {
                        printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n",
                               __func__, rdev_get_name(rdev), ret);
@@ -1341,6 +1388,9 @@ static int _regulator_disable(struct regulator_dev *rdev)
                                       __func__, rdev_get_name(rdev));
                                return ret;
                        }
+
+                       _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
+                                            NULL);
                }
 
                /* decrease our supplies ref count and disable if required */
@@ -1399,8 +1449,8 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
                        return ret;
                }
                /* notify other consumers that power has been forced off */
-               _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE,
-                       NULL);
+               _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
+                       REGULATOR_EVENT_DISABLE, NULL);
        }
 
        /* decrease our supplies ref count and disable if required */
@@ -1434,9 +1484,9 @@ EXPORT_SYMBOL_GPL(regulator_force_disable);
 
 static int _regulator_is_enabled(struct regulator_dev *rdev)
 {
-       /* sanity check */
+       /* If we don't know then assume that the regulator is always on */
        if (!rdev->desc->ops->is_enabled)
-               return -EINVAL;
+               return 1;
 
        return rdev->desc->ops->is_enabled(rdev);
 }
@@ -2451,8 +2501,15 @@ EXPORT_SYMBOL_GPL(regulator_get_init_drvdata);
 
 static int __init regulator_init(void)
 {
+       int ret;
+
        printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION);
-       return class_register(&regulator_class);
+
+       ret = class_register(&regulator_class);
+
+       regulator_dummy_init();
+
+       return ret;
 }
 
 /* init early to allow our consumers to complete system booting */
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
new file mode 100644 (file)
index 0000000..c7410bd
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * dummy.c
+ *
+ * Copyright 2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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 is useful for systems with mixed controllable and
+ * non-controllable regulators, as well as for allowing testing on
+ * systems with no controllable regulators.
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include "dummy.h"
+
+struct regulator_dev *dummy_regulator_rdev;
+
+static struct regulator_init_data dummy_initdata;
+
+static struct regulator_ops dummy_ops;
+
+static struct regulator_desc dummy_desc = {
+       .name = "dummy",
+       .id = -1,
+       .type = REGULATOR_VOLTAGE,
+       .owner = THIS_MODULE,
+       .ops = &dummy_ops,
+};
+
+static struct platform_device *dummy_pdev;
+
+void __init regulator_dummy_init(void)
+{
+       int ret;
+
+       dummy_pdev = platform_device_alloc("reg-dummy", -1);
+       if (!dummy_pdev) {
+               pr_err("Failed to allocate dummy regulator device\n");
+               return;
+       }
+
+       ret = platform_device_add(dummy_pdev);
+       if (ret != 0) {
+               pr_err("Failed to register dummy regulator device: %d\n", ret);
+               platform_device_put(dummy_pdev);
+               return;
+       }
+
+       dummy_regulator_rdev = regulator_register(&dummy_desc, NULL,
+                                                 &dummy_initdata, NULL);
+       if (IS_ERR(dummy_regulator_rdev)) {
+               ret = PTR_ERR(dummy_regulator_rdev);
+               pr_err("Failed to register regulator: %d\n", ret);
+               platform_device_unregister(dummy_pdev);
+               return;
+       }
+}
diff --git a/drivers/regulator/dummy.h b/drivers/regulator/dummy.h
new file mode 100644 (file)
index 0000000..3921c0e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * dummy.h
+ *
+ * Copyright 2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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 is useful for systems with mixed controllable and
+ * non-controllable regulators, as well as for allowing testing on
+ * systems with no controllable regulators.
+ */
+
+#ifndef _DUMMY_H
+#define _DUMMY_H
+
+struct regulator_dev;
+
+extern struct regulator_dev *dummy_regulator_rdev;
+
+#ifdef CONFIG_REGULATOR_DUMMY
+void __init regulator_dummy_init(void);
+#else
+static inline void regulator_dummy_init(void) { }
+#endif
+
+#endif
index f9f516a3028aa219a9dee0b397fc7e85fd54888d..d11f7622430bfa4a00d6ade280f1ad70edb7e93b 100644 (file)
 #include <linux/regulator/driver.h>
 #include <linux/regulator/fixed.h>
 #include <linux/gpio.h>
+#include <linux/delay.h>
 
 struct fixed_voltage_data {
        struct regulator_desc desc;
        struct regulator_dev *dev;
        int microvolts;
        int gpio;
-       unsigned enable_high:1;
-       unsigned is_enabled:1;
+       unsigned startup_delay;
+       bool enable_high;
+       bool is_enabled;
 };
 
 static int fixed_voltage_is_enabled(struct regulator_dev *dev)
@@ -47,7 +49,7 @@ static int fixed_voltage_enable(struct regulator_dev *dev)
 
        if (gpio_is_valid(data->gpio)) {
                gpio_set_value_cansleep(data->gpio, data->enable_high);
-               data->is_enabled = 1;
+               data->is_enabled = true;
        }
 
        return 0;
@@ -59,12 +61,19 @@ static int fixed_voltage_disable(struct regulator_dev *dev)
 
        if (gpio_is_valid(data->gpio)) {
                gpio_set_value_cansleep(data->gpio, !data->enable_high);
-               data->is_enabled = 0;
+               data->is_enabled = false;
        }
 
        return 0;
 }
 
+static int fixed_voltage_enable_time(struct regulator_dev *dev)
+{
+       struct fixed_voltage_data *data = rdev_get_drvdata(dev);
+
+       return data->startup_delay;
+}
+
 static int fixed_voltage_get_voltage(struct regulator_dev *dev)
 {
        struct fixed_voltage_data *data = rdev_get_drvdata(dev);
@@ -87,11 +96,12 @@ static struct regulator_ops fixed_voltage_ops = {
        .is_enabled = fixed_voltage_is_enabled,
        .enable = fixed_voltage_enable,
        .disable = fixed_voltage_disable,
+       .enable_time = fixed_voltage_enable_time,
        .get_voltage = fixed_voltage_get_voltage,
        .list_voltage = fixed_voltage_list_voltage,
 };
 
-static int regulator_fixed_voltage_probe(struct platform_device *pdev)
+static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev)
 {
        struct fixed_voltage_config *config = pdev->dev.platform_data;
        struct fixed_voltage_data *drvdata;
@@ -117,6 +127,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev)
 
        drvdata->microvolts = config->microvolts;
        drvdata->gpio = config->gpio;
+       drvdata->startup_delay = config->startup_delay;
 
        if (gpio_is_valid(config->gpio)) {
                drvdata->enable_high = config->enable_high;
@@ -163,7 +174,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev)
                /* Regulator without GPIO control is considered
                 * always enabled
                 */
-               drvdata->is_enabled = 1;
+               drvdata->is_enabled = true;
        }
 
        drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev,
@@ -191,7 +202,7 @@ err:
        return ret;
 }
 
-static int regulator_fixed_voltage_remove(struct platform_device *pdev)
+static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev)
 {
        struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev);
 
@@ -205,10 +216,11 @@ static int regulator_fixed_voltage_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver regulator_fixed_voltage_driver = {
-       .probe          = regulator_fixed_voltage_probe,
-       .remove         = regulator_fixed_voltage_remove,
+       .probe          = reg_fixed_voltage_probe,
+       .remove         = __devexit_p(reg_fixed_voltage_remove),
        .driver         = {
                .name           = "reg-fixed-voltage",
+               .owner          = THIS_MODULE,
        },
 };
 
index 4f33a0f4a179669b1619991f0cb655f85193373b..f5532ed79272c08976723687684f461f49b5b281 100644 (file)
@@ -54,7 +54,7 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val);
 #define LP3971_BUCK2_BASE 0x29
 #define LP3971_BUCK3_BASE 0x32
 
-const static int buck_base_addr[] = {
+static const int buck_base_addr[] = {
        LP3971_BUCK1_BASE,
        LP3971_BUCK2_BASE,
        LP3971_BUCK3_BASE,
@@ -63,7 +63,7 @@ const static int buck_base_addr[] = {
 #define LP3971_BUCK_TARGET_VOL1_REG(x) (buck_base_addr[x])
 #define LP3971_BUCK_TARGET_VOL2_REG(x) (buck_base_addr[x]+1)
 
-const static int buck_voltage_map[] = {
+static const int buck_voltage_map[] = {
           0,  800,  850,  900,  950, 1000, 1050, 1100,
        1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500,
        1550, 1600, 1650, 1700, 1800, 1900, 2500, 2800,
@@ -96,17 +96,17 @@ const static int buck_voltage_map[] = {
 #define LDO_VOL_CONTR_SHIFT(x) ((x & 1) << 2)
 #define LDO_VOL_CONTR_MASK 0x0f
 
-const static int ldo45_voltage_map[] = {
+static const int ldo45_voltage_map[] = {
        1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350,
        1400, 1500, 1800, 1900, 2500, 2800, 3000, 3300,
 };
 
-const static int ldo123_voltage_map[] = {
+static const int ldo123_voltage_map[] = {
        1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500,
        2600, 2700, 2800, 2900, 3000, 3100, 3200, 3300,
 };
 
-const static int *ldo_voltage_map[] = {
+static const int *ldo_voltage_map[] = {
        ldo123_voltage_map, /* LDO1 */
        ldo123_voltage_map, /* LDO2 */
        ldo123_voltage_map, /* LDO3 */
@@ -431,20 +431,20 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val)
        return ret;
 }
 
-static int setup_regulators(struct lp3971 *lp3971,
-       struct lp3971_platform_data *pdata)
+static int __devinit setup_regulators(struct lp3971 *lp3971,
+                                     struct lp3971_platform_data *pdata)
 {
        int i, err;
-       int num_regulators = pdata->num_regulators;
-       lp3971->num_regulators = num_regulators;
-       lp3971->rdev = kzalloc(sizeof(struct regulator_dev *) * num_regulators,
-               GFP_KERNEL);
+
+       lp3971->num_regulators = pdata->num_regulators;
+       lp3971->rdev = kcalloc(pdata->num_regulators,
+                               sizeof(struct regulator_dev *), GFP_KERNEL);
 
        /* Instantiate the regulators */
-       for (i = 0; i < num_regulators; i++) {
-               int id = pdata->regulators[i].id;
-               lp3971->rdev[i] = regulator_register(&regulators[id],
-                       lp3971->dev, pdata->regulators[i].initdata, lp3971);
+       for (i = 0; i < pdata->num_regulators; i++) {
+               struct lp3971_regulator_subdev *reg = &pdata->regulators[i];
+               lp3971->rdev[i] = regulator_register(&regulators[reg->id],
+                                       lp3971->dev, reg->initdata, lp3971);
 
                if (IS_ERR(lp3971->rdev[i])) {
                        err = PTR_ERR(lp3971->rdev[i]);
@@ -455,10 +455,10 @@ static int setup_regulators(struct lp3971 *lp3971,
        }
 
        return 0;
+
 error:
-       for (i = 0; i < num_regulators; i++)
-               if (lp3971->rdev[i])
-                       regulator_unregister(lp3971->rdev[i]);
+       while (--i >= 0)
+               regulator_unregister(lp3971->rdev[i]);
        kfree(lp3971->rdev);
        lp3971->rdev = NULL;
        return err;
@@ -472,15 +472,17 @@ static int __devinit lp3971_i2c_probe(struct i2c_client *i2c,
        int ret;
        u16 val;
 
-       lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL);
-       if (lp3971 == NULL) {
-               ret = -ENOMEM;
-               goto err;
+       if (!pdata) {
+               dev_dbg(&i2c->dev, "No platform init data supplied\n");
+               return -ENODEV;
        }
 
+       lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL);
+       if (lp3971 == NULL)
+               return -ENOMEM;
+
        lp3971->i2c = i2c;
        lp3971->dev = &i2c->dev;
-       i2c_set_clientdata(i2c, lp3971);
 
        mutex_init(&lp3971->io_lock);
 
@@ -493,19 +495,15 @@ static int __devinit lp3971_i2c_probe(struct i2c_client *i2c,
                goto err_detect;
        }
 
-       if (pdata) {
-               ret = setup_regulators(lp3971, pdata);
-               if (ret < 0)
-                       goto err_detect;
-       } else
-               dev_warn(lp3971->dev, "No platform init data supplied\n");
+       ret = setup_regulators(lp3971, pdata);
+       if (ret < 0)
+               goto err_detect;
 
+       i2c_set_clientdata(i2c, lp3971);
        return 0;
 
 err_detect:
-       i2c_set_clientdata(i2c, NULL);
        kfree(lp3971);
-err:
        return ret;
 }
 
@@ -513,11 +511,13 @@ static int __devexit lp3971_i2c_remove(struct i2c_client *i2c)
 {
        struct lp3971 *lp3971 = i2c_get_clientdata(i2c);
        int i;
+
+       i2c_set_clientdata(i2c, NULL);
+
        for (i = 0; i < lp3971->num_regulators; i++)
-               if (lp3971->rdev[i])
-                       regulator_unregister(lp3971->rdev[i]);
+               regulator_unregister(lp3971->rdev[i]);
+
        kfree(lp3971->rdev);
-       i2c_set_clientdata(i2c, NULL);
        kfree(lp3971);
 
        return 0;
index 2c082d3ef484614897fa5d510e30e63ed8362188..a49fc952c9a98bd7495b59957b60a139e91b0f45 100644 (file)
@@ -179,8 +179,8 @@ static struct regulator_desc max1586_reg[] = {
        },
 };
 
-static int max1586_pmic_probe(struct i2c_client *client,
-                             const struct i2c_device_id *i2c_id)
+static int __devinit max1586_pmic_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *i2c_id)
 {
        struct regulator_dev **rdev;
        struct max1586_platform_data *pdata = client->dev.platform_data;
@@ -235,7 +235,7 @@ out:
        return ret;
 }
 
-static int max1586_pmic_remove(struct i2c_client *client)
+static int __devexit max1586_pmic_remove(struct i2c_client *client)
 {
        struct regulator_dev **rdev = i2c_get_clientdata(client);
        int i;
@@ -257,9 +257,10 @@ MODULE_DEVICE_TABLE(i2c, max1586_id);
 
 static struct i2c_driver max1586_pmic_driver = {
        .probe = max1586_pmic_probe,
-       .remove = max1586_pmic_remove,
+       .remove = __devexit_p(max1586_pmic_remove),
        .driver         = {
                .name   = "max1586",
+               .owner  = THIS_MODULE,
        },
        .id_table       = max1586_id,
 };
diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c
new file mode 100644 (file)
index 0000000..3ebdf69
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Regulators driver for Maxim max8649
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ *      Haojian Zhuang <haojian.zhuang@marvell.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/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/max8649.h>
+
+#define MAX8649_DCDC_VMIN      750000          /* uV */
+#define MAX8649_DCDC_VMAX      1380000         /* uV */
+#define MAX8649_DCDC_STEP      10000           /* uV */
+#define MAX8649_VOL_MASK       0x3f
+
+/* Registers */
+#define MAX8649_MODE0          0x00
+#define MAX8649_MODE1          0x01
+#define MAX8649_MODE2          0x02
+#define MAX8649_MODE3          0x03
+#define MAX8649_CONTROL                0x04
+#define MAX8649_SYNC           0x05
+#define MAX8649_RAMP           0x06
+#define MAX8649_CHIP_ID1       0x08
+#define MAX8649_CHIP_ID2       0x09
+
+/* Bits */
+#define MAX8649_EN_PD          (1 << 7)
+#define MAX8649_VID0_PD                (1 << 6)
+#define MAX8649_VID1_PD                (1 << 5)
+#define MAX8649_VID_MASK       (3 << 5)
+
+#define MAX8649_FORCE_PWM      (1 << 7)
+#define MAX8649_SYNC_EXTCLK    (1 << 6)
+
+#define MAX8649_EXT_MASK       (3 << 6)
+
+#define MAX8649_RAMP_MASK      (7 << 5)
+#define MAX8649_RAMP_DOWN      (1 << 1)
+
+struct max8649_regulator_info {
+       struct regulator_dev    *regulator;
+       struct i2c_client       *i2c;
+       struct device           *dev;
+       struct mutex            io_lock;
+
+       int             vol_reg;
+       unsigned        mode:2; /* bit[1:0] = VID1, VID0 */
+       unsigned        extclk_freq:2;
+       unsigned        extclk:1;
+       unsigned        ramp_timing:3;
+       unsigned        ramp_down:1;
+};
+
+/* I2C operations */
+
+static inline int max8649_read_device(struct i2c_client *i2c,
+                                     int reg, int bytes, void *dest)
+{
+       unsigned char data;
+       int ret;
+
+       data = (unsigned char)reg;
+       ret = i2c_master_send(i2c, &data, 1);
+       if (ret < 0)
+               return ret;
+       ret = i2c_master_recv(i2c, dest, bytes);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static inline int max8649_write_device(struct i2c_client *i2c,
+                                      int reg, int bytes, void *src)
+{
+       unsigned char buf[bytes + 1];
+       int ret;
+
+       buf[0] = (unsigned char)reg;
+       memcpy(&buf[1], src, bytes);
+
+       ret = i2c_master_send(i2c, buf, bytes + 1);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int max8649_reg_read(struct i2c_client *i2c, int reg)
+{
+       struct max8649_regulator_info *info = i2c_get_clientdata(i2c);
+       unsigned char data;
+       int ret;
+
+       mutex_lock(&info->io_lock);
+       ret = max8649_read_device(i2c, reg, 1, &data);
+       mutex_unlock(&info->io_lock);
+
+       if (ret < 0)
+               return ret;
+       return (int)data;
+}
+
+static int max8649_set_bits(struct i2c_client *i2c, int reg,
+                           unsigned char mask, unsigned char data)
+{
+       struct max8649_regulator_info *info = i2c_get_clientdata(i2c);
+       unsigned char value;
+       int ret;
+
+       mutex_lock(&info->io_lock);
+       ret = max8649_read_device(i2c, reg, 1, &value);
+       if (ret < 0)
+               goto out;
+       value &= ~mask;
+       value |= data;
+       ret = max8649_write_device(i2c, reg, 1, &value);
+out:
+       mutex_unlock(&info->io_lock);
+       return ret;
+}
+
+static inline int check_range(int min_uV, int max_uV)
+{
+       if ((min_uV < MAX8649_DCDC_VMIN) || (max_uV > MAX8649_DCDC_VMAX)
+               || (min_uV > max_uV))
+               return -EINVAL;
+       return 0;
+}
+
+static int max8649_list_voltage(struct regulator_dev *rdev, unsigned index)
+{
+       return (MAX8649_DCDC_VMIN + index * MAX8649_DCDC_STEP);
+}
+
+static int max8649_get_voltage(struct regulator_dev *rdev)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       unsigned char data;
+       int ret;
+
+       ret = max8649_reg_read(info->i2c, info->vol_reg);
+       if (ret < 0)
+               return ret;
+       data = (unsigned char)ret & MAX8649_VOL_MASK;
+       return max8649_list_voltage(rdev, data);
+}
+
+static int max8649_set_voltage(struct regulator_dev *rdev,
+                              int min_uV, int max_uV)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       unsigned char data, mask;
+
+       if (check_range(min_uV, max_uV)) {
+               dev_err(info->dev, "invalid voltage range (%d, %d) uV\n",
+                       min_uV, max_uV);
+               return -EINVAL;
+       }
+       data = (min_uV - MAX8649_DCDC_VMIN + MAX8649_DCDC_STEP - 1)
+               / MAX8649_DCDC_STEP;
+       mask = MAX8649_VOL_MASK;
+
+       return max8649_set_bits(info->i2c, info->vol_reg, mask, data);
+}
+
+/* EN_PD means pulldown on EN input */
+static int max8649_enable(struct regulator_dev *rdev)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       return max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_EN_PD, 0);
+}
+
+/*
+ * Applied internal pulldown resistor on EN input pin.
+ * If pulldown EN pin outside, it would be better.
+ */
+static int max8649_disable(struct regulator_dev *rdev)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       return max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_EN_PD,
+                               MAX8649_EN_PD);
+}
+
+static int max8649_is_enabled(struct regulator_dev *rdev)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       int ret;
+
+       ret = max8649_reg_read(info->i2c, MAX8649_CONTROL);
+       if (ret < 0)
+               return ret;
+       return !((unsigned char)ret & MAX8649_EN_PD);
+}
+
+static int max8649_enable_time(struct regulator_dev *rdev)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       int voltage, rate, ret;
+
+       /* get voltage */
+       ret = max8649_reg_read(info->i2c, info->vol_reg);
+       if (ret < 0)
+               return ret;
+       ret &= MAX8649_VOL_MASK;
+       voltage = max8649_list_voltage(rdev, (unsigned char)ret); /* uV */
+
+       /* get rate */
+       ret = max8649_reg_read(info->i2c, MAX8649_RAMP);
+       if (ret < 0)
+               return ret;
+       ret = (ret & MAX8649_RAMP_MASK) >> 5;
+       rate = (32 * 1000) >> ret;      /* uV/uS */
+
+       return (voltage / rate);
+}
+
+static int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               max8649_set_bits(info->i2c, info->vol_reg, MAX8649_FORCE_PWM,
+                                MAX8649_FORCE_PWM);
+               break;
+       case REGULATOR_MODE_NORMAL:
+               max8649_set_bits(info->i2c, info->vol_reg,
+                                MAX8649_FORCE_PWM, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static unsigned int max8649_get_mode(struct regulator_dev *rdev)
+{
+       struct max8649_regulator_info *info = rdev_get_drvdata(rdev);
+       int ret;
+
+       ret = max8649_reg_read(info->i2c, info->vol_reg);
+       if (ret & MAX8649_FORCE_PWM)
+               return REGULATOR_MODE_FAST;
+       return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops max8649_dcdc_ops = {
+       .set_voltage    = max8649_set_voltage,
+       .get_voltage    = max8649_get_voltage,
+       .list_voltage   = max8649_list_voltage,
+       .enable         = max8649_enable,
+       .disable        = max8649_disable,
+       .is_enabled     = max8649_is_enabled,
+       .enable_time    = max8649_enable_time,
+       .set_mode       = max8649_set_mode,
+       .get_mode       = max8649_get_mode,
+
+};
+
+static struct regulator_desc dcdc_desc = {
+       .name           = "max8649",
+       .ops            = &max8649_dcdc_ops,
+       .type           = REGULATOR_VOLTAGE,
+       .n_voltages     = 1 << 6,
+       .owner          = THIS_MODULE,
+};
+
+static int __devinit max8649_regulator_probe(struct i2c_client *client,
+                                            const struct i2c_device_id *id)
+{
+       struct max8649_platform_data *pdata = client->dev.platform_data;
+       struct max8649_regulator_info *info = NULL;
+       unsigned char data;
+       int ret;
+
+       info = kzalloc(sizeof(struct max8649_regulator_info), GFP_KERNEL);
+       if (!info) {
+               dev_err(&client->dev, "No enough memory\n");
+               return -ENOMEM;
+       }
+
+       info->i2c = client;
+       info->dev = &client->dev;
+       mutex_init(&info->io_lock);
+       i2c_set_clientdata(client, info);
+
+       info->mode = pdata->mode;
+       switch (info->mode) {
+       case 0:
+               info->vol_reg = MAX8649_MODE0;
+               break;
+       case 1:
+               info->vol_reg = MAX8649_MODE1;
+               break;
+       case 2:
+               info->vol_reg = MAX8649_MODE2;
+               break;
+       case 3:
+               info->vol_reg = MAX8649_MODE3;
+               break;
+       default:
+               break;
+       }
+
+       ret = max8649_reg_read(info->i2c, MAX8649_CHIP_ID1);
+       if (ret < 0) {
+               dev_err(info->dev, "Failed to detect ID of MAX8649:%d\n",
+                       ret);
+               goto out;
+       }
+       dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", ret);
+
+       /* enable VID0 & VID1 */
+       max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_VID_MASK, 0);
+
+       /* enable/disable external clock synchronization */
+       info->extclk = pdata->extclk;
+       data = (info->extclk) ? MAX8649_SYNC_EXTCLK : 0;
+       max8649_set_bits(info->i2c, info->vol_reg, MAX8649_SYNC_EXTCLK, data);
+       if (info->extclk) {
+               /* set external clock frequency */
+               info->extclk_freq = pdata->extclk_freq;
+               max8649_set_bits(info->i2c, MAX8649_SYNC, MAX8649_EXT_MASK,
+                                info->extclk_freq);
+       }
+
+       if (pdata->ramp_timing) {
+               info->ramp_timing = pdata->ramp_timing;
+               max8649_set_bits(info->i2c, MAX8649_RAMP, MAX8649_RAMP_MASK,
+                                info->ramp_timing << 5);
+       }
+
+       info->ramp_down = pdata->ramp_down;
+       if (info->ramp_down) {
+               max8649_set_bits(info->i2c, MAX8649_RAMP, MAX8649_RAMP_DOWN,
+                                MAX8649_RAMP_DOWN);
+       }
+
+       info->regulator = regulator_register(&dcdc_desc, &client->dev,
+                                            pdata->regulator, info);
+       if (IS_ERR(info->regulator)) {
+               dev_err(info->dev, "failed to register regulator %s\n",
+                       dcdc_desc.name);
+               ret = PTR_ERR(info->regulator);
+               goto out;
+       }
+
+       dev_info(info->dev, "Max8649 regulator device is detected.\n");
+       return 0;
+out:
+       kfree(info);
+       return ret;
+}
+
+static int __devexit max8649_regulator_remove(struct i2c_client *client)
+{
+       struct max8649_regulator_info *info = i2c_get_clientdata(client);
+
+       if (info) {
+               if (info->regulator)
+                       regulator_unregister(info->regulator);
+               kfree(info);
+       }
+       i2c_set_clientdata(client, NULL);
+
+       return 0;
+}
+
+static const struct i2c_device_id max8649_id[] = {
+       { "max8649", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max8649_id);
+
+static struct i2c_driver max8649_driver = {
+       .probe          = max8649_regulator_probe,
+       .remove         = __devexit_p(max8649_regulator_remove),
+       .driver         = {
+               .name   = "max8649",
+       },
+       .id_table       = max8649_id,
+};
+
+static int __init max8649_init(void)
+{
+       return i2c_add_driver(&max8649_driver);
+}
+subsys_initcall(max8649_init);
+
+static void __exit max8649_exit(void)
+{
+       i2c_del_driver(&max8649_driver);
+}
+module_exit(max8649_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MAXIM 8649 voltage regulator driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+
index acc2fb7b6087783912110796243c99ee7f6fd8c1..f12f1bb62138f95851ae7da9289a1b89d159924c 100644 (file)
@@ -345,8 +345,8 @@ static struct regulator_desc max8660_reg[] = {
        },
 };
 
-static int max8660_probe(struct i2c_client *client,
-                             const struct i2c_device_id *i2c_id)
+static int __devinit max8660_probe(struct i2c_client *client,
+                                  const struct i2c_device_id *i2c_id)
 {
        struct regulator_dev **rdev;
        struct max8660_platform_data *pdata = client->dev.platform_data;
@@ -354,7 +354,7 @@ static int max8660_probe(struct i2c_client *client,
        int boot_on, i, id, ret = -EINVAL;
 
        if (pdata->num_subdevs > MAX8660_V_END) {
-               dev_err(&client->dev, "Too much regulators found!\n");
+               dev_err(&client->dev, "Too many regulators found!\n");
                goto out;
        }
 
@@ -462,7 +462,7 @@ out:
        return ret;
 }
 
-static int max8660_remove(struct i2c_client *client)
+static int __devexit max8660_remove(struct i2c_client *client)
 {
        struct regulator_dev **rdev = i2c_get_clientdata(client);
        int i;
@@ -485,9 +485,10 @@ MODULE_DEVICE_TABLE(i2c, max8660_id);
 
 static struct i2c_driver max8660_driver = {
        .probe = max8660_probe,
-       .remove = max8660_remove,
+       .remove = __devexit_p(max8660_remove),
        .driver         = {
                .name   = "max8660",
+               .owner  = THIS_MODULE,
        },
        .id_table       = max8660_id,
 };
index 39c4953000458d2ce7cba8cc9958a6e247a729b0..f7b81845a196223f5fdf5051115fa9ecd2a69d7f 100644 (file)
@@ -2,6 +2,7 @@
  * Regulator Driver for Freescale MC13783 PMIC
  *
  * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright 2009 Alberto Panizzo <maramaopercheseimorto@gmail.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
 #include <linux/init.h>
 #include <linux/err.h>
 
-#define MC13783_REG_SWITCHERS4                 28
-#define MC13783_REG_SWITCHERS4_PLLEN                   (1 << 18)
-
 #define MC13783_REG_SWITCHERS5                 29
 #define MC13783_REG_SWITCHERS5_SW3EN                   (1 << 20)
+#define MC13783_REG_SWITCHERS5_SW3VSEL                 18
+#define MC13783_REG_SWITCHERS5_SW3VSEL_M               (3 << 18)
+
+#define MC13783_REG_REGULATORSETTING0          30
+#define MC13783_REG_REGULATORSETTING0_VIOLOVSEL                2
+#define MC13783_REG_REGULATORSETTING0_VDIGVSEL         4
+#define MC13783_REG_REGULATORSETTING0_VGENVSEL         6
+#define MC13783_REG_REGULATORSETTING0_VRFDIGVSEL       9
+#define MC13783_REG_REGULATORSETTING0_VRFREFVSEL       11
+#define MC13783_REG_REGULATORSETTING0_VRFCPVSEL                13
+#define MC13783_REG_REGULATORSETTING0_VSIMVSEL         14
+#define MC13783_REG_REGULATORSETTING0_VESIMVSEL                15
+#define MC13783_REG_REGULATORSETTING0_VCAMVSEL         16
+
+#define MC13783_REG_REGULATORSETTING0_VIOLOVSEL_M      (3 << 2)
+#define MC13783_REG_REGULATORSETTING0_VDIGVSEL_M       (3 << 4)
+#define MC13783_REG_REGULATORSETTING0_VGENVSEL_M       (7 << 6)
+#define MC13783_REG_REGULATORSETTING0_VRFDIGVSEL_M     (3 << 9)
+#define MC13783_REG_REGULATORSETTING0_VRFREFVSEL_M     (3 << 11)
+#define MC13783_REG_REGULATORSETTING0_VRFCPVSEL_M      (1 << 13)
+#define MC13783_REG_REGULATORSETTING0_VSIMVSEL_M       (1 << 14)
+#define MC13783_REG_REGULATORSETTING0_VESIMVSEL_M      (1 << 15)
+#define MC13783_REG_REGULATORSETTING0_VCAMVSEL_M       (7 << 16)
+
+#define MC13783_REG_REGULATORSETTING1          31
+#define MC13783_REG_REGULATORSETTING1_VVIBVSEL         0
+#define MC13783_REG_REGULATORSETTING1_VRF1VSEL         2
+#define MC13783_REG_REGULATORSETTING1_VRF2VSEL         4
+#define MC13783_REG_REGULATORSETTING1_VMMC1VSEL                6
+#define MC13783_REG_REGULATORSETTING1_VMMC2VSEL                9
+
+#define MC13783_REG_REGULATORSETTING1_VVIBVSEL_M       (3 << 0)
+#define MC13783_REG_REGULATORSETTING1_VRF1VSEL_M       (3 << 2)
+#define MC13783_REG_REGULATORSETTING1_VRF2VSEL_M       (3 << 4)
+#define MC13783_REG_REGULATORSETTING1_VMMC1VSEL_M      (7 << 6)
+#define MC13783_REG_REGULATORSETTING1_VMMC2VSEL_M      (7 << 9)
 
 #define MC13783_REG_REGULATORMODE0             32
 #define MC13783_REG_REGULATORMODE0_VAUDIOEN            (1 << 0)
 #define MC13783_REG_POWERMISC_GPO2EN                   (1 << 8)
 #define MC13783_REG_POWERMISC_GPO3EN                   (1 << 10)
 #define MC13783_REG_POWERMISC_GPO4EN                   (1 << 12)
+#define MC13783_REG_POWERMISC_PWGT1SPIEN               (1 << 15)
+#define MC13783_REG_POWERMISC_PWGT2SPIEN               (1 << 16)
+
+#define MC13783_REG_POWERMISC_PWGTSPI_M                        (3 << 15)
+
 
 struct mc13783_regulator {
        struct regulator_desc desc;
        int reg;
        int enable_bit;
+       int vsel_reg;
+       int vsel_shift;
+       int vsel_mask;
+       int const *voltages;
+};
+
+/* Voltage Values */
+static const int const mc13783_sw3_val[] = {
+       5000000, 5000000, 5000000, 5500000,
+};
+
+static const int const mc13783_vaudio_val[] = {
+       2775000,
+};
+
+static const int const mc13783_viohi_val[] = {
+       2775000,
+};
+
+static const int const mc13783_violo_val[] = {
+       1200000, 1300000, 1500000, 1800000,
+};
+
+static const int const mc13783_vdig_val[] = {
+       1200000, 1300000, 1500000, 1800000,
+};
+
+static const int const mc13783_vgen_val[] = {
+       1200000, 1300000, 1500000, 1800000,
+       1100000, 2000000, 2775000, 2400000,
+};
+
+static const int const mc13783_vrfdig_val[] = {
+       1200000, 1500000, 1800000, 1875000,
+};
+
+static const int const mc13783_vrfref_val[] = {
+       2475000, 2600000, 2700000, 2775000,
+};
+
+static const int const mc13783_vrfcp_val[] = {
+       2700000, 2775000,
+};
+
+static const int const mc13783_vsim_val[] = {
+       1800000, 2900000, 3000000,
+};
+
+static const int const mc13783_vesim_val[] = {
+       1800000, 2900000,
+};
+
+static const int const mc13783_vcam_val[] = {
+       1500000, 1800000, 2500000, 2550000,
+       2600000, 2750000, 2800000, 3000000,
+};
+
+static const int const mc13783_vrfbg_val[] = {
+       1250000,
+};
+
+static const int const mc13783_vvib_val[] = {
+       1300000, 1800000, 2000000, 3000000,
+};
+
+static const int const mc13783_vmmc_val[] = {
+       1600000, 1800000, 2000000, 2600000,
+       2700000, 2800000, 2900000, 3000000,
+};
+
+static const int const mc13783_vrf_val[] = {
+       1500000, 1875000, 2700000, 2775000,
+};
+
+static const int const mc13783_gpo_val[] = {
+       3100000,
+};
+
+static const int const mc13783_pwgtdrv_val[] = {
+       5500000,
 };
 
 static struct regulator_ops mc13783_regulator_ops;
+static struct regulator_ops mc13783_fixed_regulator_ops;
+static struct regulator_ops mc13783_gpo_regulator_ops;
 
-#define MC13783_DEFINE(prefix, _name, _reg)                            \
+#define MC13783_DEFINE(prefix, _name, _reg, _vsel_reg, _voltages)      \
        [MC13783_ ## prefix ## _ ## _name] = {                          \
                .desc = {                                               \
                        .name = #prefix "_" #_name,                     \
+                       .n_voltages = ARRAY_SIZE(_voltages),            \
                        .ops = &mc13783_regulator_ops,                  \
                        .type = REGULATOR_VOLTAGE,                      \
                        .id = MC13783_ ## prefix ## _ ## _name,         \
@@ -68,40 +190,92 @@ static struct regulator_ops mc13783_regulator_ops;
                },                                                      \
                .reg = MC13783_REG_ ## _reg,                            \
                .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
+               .vsel_reg = MC13783_REG_ ## _vsel_reg,                  \
+               .vsel_shift = MC13783_REG_ ## _vsel_reg ## _ ## _name ## VSEL,\
+               .vsel_mask = MC13783_REG_ ## _vsel_reg ## _ ## _name ## VSEL_M,\
+               .voltages =  _voltages,                                 \
+       }
+
+#define MC13783_FIXED_DEFINE(prefix, _name, _reg, _voltages)           \
+       [MC13783_ ## prefix ## _ ## _name] = {                          \
+               .desc = {                                               \
+                       .name = #prefix "_" #_name,                     \
+                       .n_voltages = ARRAY_SIZE(_voltages),            \
+                       .ops = &mc13783_fixed_regulator_ops,            \
+                       .type = REGULATOR_VOLTAGE,                      \
+                       .id = MC13783_ ## prefix ## _ ## _name,         \
+                       .owner = THIS_MODULE,                           \
+               },                                                      \
+               .reg = MC13783_REG_ ## _reg,                            \
+               .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
+               .voltages =  _voltages,                                 \
        }
 
-#define MC13783_DEFINE_SW(_name, _reg) MC13783_DEFINE(SW, _name, _reg)
-#define MC13783_DEFINE_REGU(_name, _reg) MC13783_DEFINE(REGU, _name, _reg)
+#define MC13783_GPO_DEFINE(prefix, _name, _reg,  _voltages)            \
+       [MC13783_ ## prefix ## _ ## _name] = {                          \
+               .desc = {                                               \
+                       .name = #prefix "_" #_name,                     \
+                       .n_voltages = ARRAY_SIZE(_voltages),            \
+                       .ops = &mc13783_gpo_regulator_ops,              \
+                       .type = REGULATOR_VOLTAGE,                      \
+                       .id = MC13783_ ## prefix ## _ ## _name,         \
+                       .owner = THIS_MODULE,                           \
+               },                                                      \
+               .reg = MC13783_REG_ ## _reg,                            \
+               .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
+               .voltages =  _voltages,                                 \
+       }
+
+#define MC13783_DEFINE_SW(_name, _reg, _vsel_reg, _voltages)           \
+       MC13783_DEFINE(SW, _name, _reg, _vsel_reg, _voltages)
+#define MC13783_DEFINE_REGU(_name, _reg, _vsel_reg, _voltages)         \
+       MC13783_DEFINE(REGU, _name, _reg, _vsel_reg, _voltages)
 
 static struct mc13783_regulator mc13783_regulators[] = {
-       MC13783_DEFINE_SW(SW3, SWITCHERS5),
-       MC13783_DEFINE_SW(PLL, SWITCHERS4),
-
-       MC13783_DEFINE_REGU(VAUDIO, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VIOHI, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VDIG, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VGEN, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0),
-       MC13783_DEFINE_REGU(VSIM, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VESIM, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VCAM, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VRFBG, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VVIB, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VRF1, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VRF2, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1),
-       MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1),
-       MC13783_DEFINE_REGU(GPO1, POWERMISC),
-       MC13783_DEFINE_REGU(GPO2, POWERMISC),
-       MC13783_DEFINE_REGU(GPO3, POWERMISC),
-       MC13783_DEFINE_REGU(GPO4, POWERMISC),
+       MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val),
+
+       MC13783_FIXED_DEFINE(REGU, VAUDIO, REGULATORMODE0, mc13783_vaudio_val),
+       MC13783_FIXED_DEFINE(REGU, VIOHI, REGULATORMODE0, mc13783_viohi_val),
+       MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0, REGULATORSETTING0,   \
+                           mc13783_violo_val),
+       MC13783_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0,    \
+                           mc13783_vdig_val),
+       MC13783_DEFINE_REGU(VGEN, REGULATORMODE0, REGULATORSETTING0,    \
+                           mc13783_vgen_val),
+       MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0, REGULATORSETTING0,  \
+                           mc13783_vrfdig_val),
+       MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0, REGULATORSETTING0,  \
+                           mc13783_vrfref_val),
+       MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0, REGULATORSETTING0,   \
+                           mc13783_vrfcp_val),
+       MC13783_DEFINE_REGU(VSIM, REGULATORMODE1, REGULATORSETTING0,    \
+                           mc13783_vsim_val),
+       MC13783_DEFINE_REGU(VESIM, REGULATORMODE1, REGULATORSETTING0,   \
+                           mc13783_vesim_val),
+       MC13783_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0,    \
+                           mc13783_vcam_val),
+       MC13783_FIXED_DEFINE(REGU, VRFBG, REGULATORMODE1, mc13783_vrfbg_val),
+       MC13783_DEFINE_REGU(VVIB, REGULATORMODE1, REGULATORSETTING1,    \
+                           mc13783_vvib_val),
+       MC13783_DEFINE_REGU(VRF1, REGULATORMODE1, REGULATORSETTING1,    \
+                           mc13783_vrf_val),
+       MC13783_DEFINE_REGU(VRF2, REGULATORMODE1, REGULATORSETTING1,    \
+                           mc13783_vrf_val),
+       MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1, REGULATORSETTING1,   \
+                           mc13783_vmmc_val),
+       MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1,   \
+                           mc13783_vmmc_val),
+       MC13783_GPO_DEFINE(REGU, GPO1, POWERMISC, mc13783_gpo_val),
+       MC13783_GPO_DEFINE(REGU, GPO2, POWERMISC, mc13783_gpo_val),
+       MC13783_GPO_DEFINE(REGU, GPO3, POWERMISC, mc13783_gpo_val),
+       MC13783_GPO_DEFINE(REGU, GPO4, POWERMISC, mc13783_gpo_val),
+       MC13783_GPO_DEFINE(REGU, PWGT1SPI, POWERMISC, mc13783_pwgtdrv_val),
+       MC13783_GPO_DEFINE(REGU, PWGT2SPI, POWERMISC, mc13783_pwgtdrv_val),
 };
 
 struct mc13783_regulator_priv {
        struct mc13783 *mc13783;
+       u32 powermisc_pwgt_state;
        struct regulator_dev *regulators[];
 };
 
@@ -154,10 +328,241 @@ static int mc13783_regulator_is_enabled(struct regulator_dev *rdev)
        return (val & mc13783_regulators[id].enable_bit) != 0;
 }
 
+static int mc13783_regulator_list_voltage(struct regulator_dev *rdev,
+                                               unsigned selector)
+{
+       int id = rdev_get_id(rdev);
+
+       if (selector >= mc13783_regulators[id].desc.n_voltages)
+               return -EINVAL;
+
+       return mc13783_regulators[id].voltages[selector];
+}
+
+static int mc13783_get_best_voltage_index(struct regulator_dev *rdev,
+                                               int min_uV, int max_uV)
+{
+       int reg_id = rdev_get_id(rdev);
+       int i;
+       int bestmatch;
+       int bestindex;
+
+       /*
+        * Locate the minimum voltage fitting the criteria on
+        * this regulator. The switchable voltages are not
+        * in strict falling order so we need to check them
+        * all for the best match.
+        */
+       bestmatch = INT_MAX;
+       bestindex = -1;
+       for (i = 0; i < mc13783_regulators[reg_id].desc.n_voltages; i++) {
+               if (mc13783_regulators[reg_id].voltages[i] >= min_uV &&
+                   mc13783_regulators[reg_id].voltages[i] < bestmatch) {
+                       bestmatch = mc13783_regulators[reg_id].voltages[i];
+                       bestindex = i;
+               }
+       }
+
+       if (bestindex < 0 || bestmatch > max_uV) {
+               dev_warn(&rdev->dev, "no possible value for %d<=x<=%d uV\n",
+                               min_uV, max_uV);
+               return -EINVAL;
+       }
+       return bestindex;
+}
+
+static int mc13783_regulator_set_voltage(struct regulator_dev *rdev,
+                                               int min_uV, int max_uV)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int value, id = rdev_get_id(rdev);
+       int ret;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
+               __func__, id, min_uV, max_uV);
+
+       /* Find the best index */
+       value = mc13783_get_best_voltage_index(rdev, min_uV, max_uV);
+       dev_dbg(rdev_get_dev(rdev), "%s best value: %d \n", __func__, value);
+       if (value < 0)
+               return value;
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].vsel_reg,
+                       mc13783_regulators[id].vsel_mask,
+                       value << mc13783_regulators[id].vsel_shift);
+       mc13783_unlock(priv->mc13783);
+
+       return ret;
+}
+
+static int mc13783_regulator_get_voltage(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int ret, id = rdev_get_id(rdev);
+       unsigned int val;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_reg_read(priv->mc13783,
+                               mc13783_regulators[id].vsel_reg, &val);
+       mc13783_unlock(priv->mc13783);
+
+       if (ret)
+               return ret;
+
+       val = (val & mc13783_regulators[id].vsel_mask)
+               >> mc13783_regulators[id].vsel_shift;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
+
+       BUG_ON(val < 0 || val > mc13783_regulators[id].desc.n_voltages);
+
+       return mc13783_regulators[id].voltages[val];
+}
+
 static struct regulator_ops mc13783_regulator_ops = {
        .enable = mc13783_regulator_enable,
        .disable = mc13783_regulator_disable,
        .is_enabled = mc13783_regulator_is_enabled,
+       .list_voltage = mc13783_regulator_list_voltage,
+       .set_voltage = mc13783_regulator_set_voltage,
+       .get_voltage = mc13783_regulator_get_voltage,
+};
+
+static int mc13783_fixed_regulator_set_voltage(struct regulator_dev *rdev,
+                                               int min_uV, int max_uV)
+{
+       int id = rdev_get_id(rdev);
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n",
+               __func__, id, min_uV, max_uV);
+
+       if (min_uV > mc13783_regulators[id].voltages[0] &&
+           max_uV < mc13783_regulators[id].voltages[0])
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static int mc13783_fixed_regulator_get_voltage(struct regulator_dev *rdev)
+{
+       int id = rdev_get_id(rdev);
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+       return mc13783_regulators[id].voltages[0];
+}
+
+static struct regulator_ops mc13783_fixed_regulator_ops = {
+       .enable = mc13783_regulator_enable,
+       .disable = mc13783_regulator_disable,
+       .is_enabled = mc13783_regulator_is_enabled,
+       .list_voltage = mc13783_regulator_list_voltage,
+       .set_voltage = mc13783_fixed_regulator_set_voltage,
+       .get_voltage = mc13783_fixed_regulator_get_voltage,
+};
+
+int mc13783_powermisc_rmw(struct mc13783_regulator_priv *priv, u32 mask,
+                                                                       u32 val)
+{
+       struct mc13783 *mc13783 = priv->mc13783;
+       int ret;
+       u32 valread;
+
+       BUG_ON(val & ~mask);
+
+       ret = mc13783_reg_read(mc13783, MC13783_REG_POWERMISC, &valread);
+       if (ret)
+               return ret;
+
+       /* Update the stored state for Power Gates. */
+       priv->powermisc_pwgt_state =
+                               (priv->powermisc_pwgt_state & ~mask) | val;
+       priv->powermisc_pwgt_state &= MC13783_REG_POWERMISC_PWGTSPI_M;
+
+       /* Construct the new register value */
+       valread = (valread & ~mask) | val;
+       /* Overwrite the PWGTxEN with the stored version */
+       valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
+                                               priv->powermisc_pwgt_state;
+
+       return mc13783_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+}
+
+static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev);
+       int ret;
+       u32 en_val = mc13783_regulators[id].enable_bit;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+       /* Power Gate enable value is 0 */
+       if (id == MC13783_REGU_PWGT1SPI ||
+           id == MC13783_REGU_PWGT2SPI)
+               en_val = 0;
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_powermisc_rmw(priv, mc13783_regulators[id].enable_bit,
+                                       en_val);
+       mc13783_unlock(priv->mc13783);
+
+       return ret;
+}
+
+static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev);
+       int ret;
+       u32 dis_val = 0;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+       /* Power Gate disable value is 1 */
+       if (id == MC13783_REGU_PWGT1SPI ||
+           id == MC13783_REGU_PWGT2SPI)
+               dis_val = mc13783_regulators[id].enable_bit;
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_powermisc_rmw(priv, mc13783_regulators[id].enable_bit,
+                                       dis_val);
+       mc13783_unlock(priv->mc13783);
+
+       return ret;
+}
+
+static int mc13783_gpo_regulator_is_enabled(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int ret, id = rdev_get_id(rdev);
+       unsigned int val;
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
+       mc13783_unlock(priv->mc13783);
+
+       if (ret)
+               return ret;
+
+       /* Power Gates state is stored in powermisc_pwgt_state
+        * where the meaning of bits is negated */
+       val = (val & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
+             (priv->powermisc_pwgt_state ^ MC13783_REG_POWERMISC_PWGTSPI_M);
+
+       return (val & mc13783_regulators[id].enable_bit) != 0;
+}
+
+static struct regulator_ops mc13783_gpo_regulator_ops = {
+       .enable = mc13783_gpo_regulator_enable,
+       .disable = mc13783_gpo_regulator_disable,
+       .is_enabled = mc13783_gpo_regulator_is_enabled,
+       .list_voltage = mc13783_regulator_list_voltage,
+       .set_voltage = mc13783_fixed_regulator_set_voltage,
+       .get_voltage = mc13783_fixed_regulator_get_voltage,
 };
 
 static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
index 33d7d899e03073597b1f80f5ab10e85925b6e0c8..29d0566379ae452fb3a771747b49abad92a73333 100644 (file)
@@ -288,16 +288,18 @@ static int __devexit pcap_regulator_remove(struct platform_device *pdev)
        struct regulator_dev *rdev = platform_get_drvdata(pdev);
 
        regulator_unregister(rdev);
+       platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
 static struct platform_driver pcap_regulator_driver = {
        .driver = {
-               .name = "pcap-regulator",
+               .name   = "pcap-regulator",
+               .owner  = THIS_MODULE,
        },
-       .probe = pcap_regulator_probe,
-       .remove = __devexit_p(pcap_regulator_remove),
+       .probe  = pcap_regulator_probe,
+       .remove = __devexit_p(pcap_regulator_remove),
 };
 
 static int __init pcap_regulator_init(void)
index 07fda0a75adf4aa0ef9ae012fea8a1fb50cb97f4..1f183543bdbd68fa2570cba036438d274f721837 100644 (file)
@@ -457,8 +457,8 @@ static struct regulator_ops tps65023_ldo_ops = {
        .list_voltage = tps65023_ldo_list_voltage,
 };
 
-static
-int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int __devinit tps_65023_probe(struct i2c_client *client,
+                                    const struct i2c_device_id *id)
 {
        static int desc_id;
        const struct tps_info *info = (void *)id->driver_data;
@@ -466,6 +466,7 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id)
        struct regulator_dev *rdev;
        struct tps_pmic *tps;
        int i;
+       int error;
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -EIO;
@@ -475,7 +476,6 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id)
         * coming from the board-evm file.
         */
        init_data = client->dev.platform_data;
-
        if (!init_data)
                return -EIO;
 
@@ -502,21 +502,12 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
                /* Register the regulators */
                rdev = regulator_register(&tps->desc[i], &client->dev,
-                                                               init_data, tps);
+                                         init_data, tps);
                if (IS_ERR(rdev)) {
                        dev_err(&client->dev, "failed to register %s\n",
                                id->name);
-
-                       /* Unregister */
-                       while (i)
-                               regulator_unregister(tps->rdev[--i]);
-
-                       tps->client = NULL;
-
-                       /* clear the client data in i2c */
-                       i2c_set_clientdata(client, NULL);
-                       kfree(tps);
-                       return PTR_ERR(rdev);
+                       error = PTR_ERR(rdev);
+                       goto fail;
                }
 
                /* Save regulator for cleanup */
@@ -526,6 +517,13 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id)
        i2c_set_clientdata(client, tps);
 
        return 0;
+
+ fail:
+       while (--i >= 0)
+               regulator_unregister(tps->rdev[i]);
+
+       kfree(tps);
+       return error;
 }
 
 /**
@@ -539,13 +537,12 @@ static int __devexit tps_65023_remove(struct i2c_client *client)
        struct tps_pmic *tps = i2c_get_clientdata(client);
        int i;
 
+       /* clear the client data in i2c */
+       i2c_set_clientdata(client, NULL);
+
        for (i = 0; i < TPS65023_NUM_REGULATOR; i++)
                regulator_unregister(tps->rdev[i]);
 
-       tps->client = NULL;
-
-       /* clear the client data in i2c */
-       i2c_set_clientdata(client, NULL);
        kfree(tps);
 
        return 0;
index f8a6dfbef75189301872a9408ffc56ce82bbe43b..c2a9539acd723377c8f82f91a97448bdde14e742 100644 (file)
@@ -538,8 +538,8 @@ static struct regulator_ops tps6507x_ldo_ops = {
        .list_voltage = tps6507x_ldo_list_voltage,
 };
 
-static
-int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int __devinit tps_6507x_probe(struct i2c_client *client,
+                                    const struct i2c_device_id *id)
 {
        static int desc_id;
        const struct tps_info *info = (void *)id->driver_data;
@@ -547,6 +547,7 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id)
        struct regulator_dev *rdev;
        struct tps_pmic *tps;
        int i;
+       int error;
 
        if (!i2c_check_functionality(client->adapter,
                                I2C_FUNC_SMBUS_BYTE_DATA))
@@ -557,7 +558,6 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id)
         * coming from the board-evm file.
         */
        init_data = client->dev.platform_data;
-
        if (!init_data)
                return -EIO;
 
@@ -586,18 +586,8 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id)
                if (IS_ERR(rdev)) {
                        dev_err(&client->dev, "failed to register %s\n",
                                id->name);
-
-                       /* Unregister */
-                       while (i)
-                               regulator_unregister(tps->rdev[--i]);
-
-                       tps->client = NULL;
-
-                       /* clear the client data in i2c */
-                       i2c_set_clientdata(client, NULL);
-
-                       kfree(tps);
-                       return PTR_ERR(rdev);
+                       error = PTR_ERR(rdev);
+                       goto fail;
                }
 
                /* Save regulator for cleanup */
@@ -607,6 +597,13 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id)
        i2c_set_clientdata(client, tps);
 
        return 0;
+
+fail:
+       while (--i >= 0)
+               regulator_unregister(tps->rdev[i]);
+
+       kfree(tps);
+       return error;
 }
 
 /**
@@ -620,13 +617,12 @@ static int __devexit tps_6507x_remove(struct i2c_client *client)
        struct tps_pmic *tps = i2c_get_clientdata(client);
        int i;
 
+       /* clear the client data in i2c */
+       i2c_set_clientdata(client, NULL);
+
        for (i = 0; i < TPS6507X_NUM_REGULATOR; i++)
                regulator_unregister(tps->rdev[i]);
 
-       tps->client = NULL;
-
-       /* clear the client data in i2c */
-       i2c_set_clientdata(client, NULL);
        kfree(tps);
 
        return 0;
index 7e674859bd59b366288d43bf9baf2bc13dab8b11..9729d760fb4ddc742771cf5bf4d2c71097d0c456 100644 (file)
@@ -519,19 +519,19 @@ static struct twlreg_info twl_regs[] = {
        /* 6030 REG with base as PMC Slave Misc : 0x0030 */
        /* Turnon-delay and remap configuration values for 6030 are not
           verified since the specification is not public */
-       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1, 0, 0x08),
-       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2, 0, 0x08),
-       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3, 0, 0x08),
-       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4, 0, 0x08),
-       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5, 0, 0x08),
-       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7, 0, 0x08),
-       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x08),
-       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x08),
-       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x08),
-       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x08)
+       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1, 0, 0x21),
+       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2, 0, 0x21),
+       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3, 0, 0x21),
+       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4, 0, 0x21),
+       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5, 0, 0x21),
+       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7, 0, 0x21),
+       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x21),
+       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x21),
+       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x21),
+       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21)
 };
 
-static int twlreg_probe(struct platform_device *pdev)
+static int __devinit twlreg_probe(struct platform_device *pdev)
 {
        int                             i;
        struct twlreg_info              *info;
index addc032c84bfea6e5aef207a796408df025a12a5..d96cecaac73d7aeb5b1e2f0dc9f599764148a078 100644 (file)
@@ -19,7 +19,7 @@
 struct virtual_consumer_data {
        struct mutex lock;
        struct regulator *regulator;
-       int enabled;
+       bool enabled;
        int min_uV;
        int max_uV;
        int min_uA;
@@ -49,7 +49,7 @@ static void update_voltage_constraints(struct device *dev,
                dev_dbg(dev, "Enabling regulator\n");
                ret = regulator_enable(data->regulator);
                if (ret == 0)
-                       data->enabled = 1;
+                       data->enabled = true;
                else
                        dev_err(dev, "regulator_enable() failed: %d\n",
                                ret);
@@ -59,7 +59,7 @@ static void update_voltage_constraints(struct device *dev,
                dev_dbg(dev, "Disabling regulator\n");
                ret = regulator_disable(data->regulator);
                if (ret == 0)
-                       data->enabled = 0;
+                       data->enabled = false;
                else
                        dev_err(dev, "regulator_disable() failed: %d\n",
                                ret);
@@ -89,7 +89,7 @@ static void update_current_limit_constraints(struct device *dev,
                dev_dbg(dev, "Enabling regulator\n");
                ret = regulator_enable(data->regulator);
                if (ret == 0)
-                       data->enabled = 1;
+                       data->enabled = true;
                else
                        dev_err(dev, "regulator_enable() failed: %d\n",
                                ret);
@@ -99,7 +99,7 @@ static void update_current_limit_constraints(struct device *dev,
                dev_dbg(dev, "Disabling regulator\n");
                ret = regulator_disable(data->regulator);
                if (ret == 0)
-                       data->enabled = 0;
+                       data->enabled = false;
                else
                        dev_err(dev, "regulator_disable() failed: %d\n",
                                ret);
@@ -270,24 +270,28 @@ static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA);
 static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA);
 static DEVICE_ATTR(mode, 0666, show_mode, set_mode);
 
-static struct device_attribute *attributes[] = {
-       &dev_attr_min_microvolts,
-       &dev_attr_max_microvolts,
-       &dev_attr_min_microamps,
-       &dev_attr_max_microamps,
-       &dev_attr_mode,
+static struct attribute *regulator_virtual_attributes[] = {
+       &dev_attr_min_microvolts.attr,
+       &dev_attr_max_microvolts.attr,
+       &dev_attr_min_microamps.attr,
+       &dev_attr_max_microamps.attr,
+       &dev_attr_mode.attr,
+       NULL
 };
 
-static int regulator_virtual_consumer_probe(struct platform_device *pdev)
+static const struct attribute_group regulator_virtual_attr_group = {
+       .attrs  = regulator_virtual_attributes,
+};
+
+static int __devinit regulator_virtual_probe(struct platform_device *pdev)
 {
        char *reg_id = pdev->dev.platform_data;
        struct virtual_consumer_data *drvdata;
-       int ret, i;
+       int ret;
 
        drvdata = kzalloc(sizeof(struct virtual_consumer_data), GFP_KERNEL);
-       if (drvdata == NULL) {
+       if (drvdata == NULL)
                return -ENOMEM;
-       }
 
        mutex_init(&drvdata->lock);
 
@@ -299,13 +303,12 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev)
                goto err;
        }
 
-       for (i = 0; i < ARRAY_SIZE(attributes); i++) {
-               ret = device_create_file(&pdev->dev, attributes[i]);
-               if (ret != 0) {
-                       dev_err(&pdev->dev, "Failed to create attr %d: %d\n",
-                               i, ret);
-                       goto err_regulator;
-               }
+       ret = sysfs_create_group(&pdev->dev.kobj,
+                                &regulator_virtual_attr_group);
+       if (ret != 0) {
+               dev_err(&pdev->dev,
+                       "Failed to create attribute group: %d\n", ret);
+               goto err_regulator;
        }
 
        drvdata->mode = regulator_get_mode(drvdata->regulator);
@@ -317,37 +320,36 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev)
 err_regulator:
        regulator_put(drvdata->regulator);
 err:
-       for (i = 0; i < ARRAY_SIZE(attributes); i++)
-               device_remove_file(&pdev->dev, attributes[i]);
        kfree(drvdata);
        return ret;
 }
 
-static int regulator_virtual_consumer_remove(struct platform_device *pdev)
+static int __devexit regulator_virtual_remove(struct platform_device *pdev)
 {
        struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);
-       int i;
 
-       for (i = 0; i < ARRAY_SIZE(attributes); i++)
-               device_remove_file(&pdev->dev, attributes[i]);
+       sysfs_remove_group(&pdev->dev.kobj, &regulator_virtual_attr_group);
+
        if (drvdata->enabled)
                regulator_disable(drvdata->regulator);
        regulator_put(drvdata->regulator);
 
        kfree(drvdata);
 
+       platform_set_drvdata(pdev, NULL);
+
        return 0;
 }
 
 static struct platform_driver regulator_virtual_consumer_driver = {
-       .probe          = regulator_virtual_consumer_probe,
-       .remove         = regulator_virtual_consumer_remove,
+       .probe          = regulator_virtual_probe,
+       .remove         = __devexit_p(regulator_virtual_remove),
        .driver         = {
                .name           = "reg-virt-consumer",
+               .owner          = THIS_MODULE,
        },
 };
 
-
 static int __init regulator_virtual_consumer_init(void)
 {
        return platform_driver_register(&regulator_virtual_consumer_driver);
index 0a6577577e8d674252ea8b2a025f4a23515c5681..6e18e56d850ba7159312938baf59d6f50ecc3fa3 100644 (file)
@@ -600,6 +600,8 @@ 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);
+
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
        regulator_unregister(dcdc->regulator);
@@ -615,6 +617,7 @@ static struct platform_driver wm831x_buckv_driver = {
        .remove = __devexit_p(wm831x_buckv_remove),
        .driver         = {
                .name   = "wm831x-buckv",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -769,6 +772,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
        struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
        struct wm831x *wm831x = dcdc->wm831x;
 
+       platform_set_drvdata(pdev, NULL);
+
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
        regulator_unregister(dcdc->regulator);
        kfree(dcdc);
@@ -781,6 +786,7 @@ static struct platform_driver wm831x_buckp_driver = {
        .remove = __devexit_p(wm831x_buckp_remove),
        .driver         = {
                .name   = "wm831x-buckp",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -895,6 +901,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
        struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
        struct wm831x *wm831x = dcdc->wm831x;
 
+       platform_set_drvdata(pdev, NULL);
+
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
        regulator_unregister(dcdc->regulator);
        kfree(dcdc);
@@ -907,6 +915,7 @@ static struct platform_driver wm831x_boostp_driver = {
        .remove = __devexit_p(wm831x_boostp_remove),
        .driver         = {
                .name   = "wm831x-boostp",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -979,6 +988,8 @@ static __devexit int wm831x_epe_remove(struct platform_device *pdev)
 {
        struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
 
+       platform_set_drvdata(pdev, NULL);
+
        regulator_unregister(dcdc->regulator);
        kfree(dcdc);
 
@@ -990,6 +1001,7 @@ static struct platform_driver wm831x_epe_driver = {
        .remove = __devexit_p(wm831x_epe_remove),
        .driver         = {
                .name   = "wm831x-epe",
+               .owner  = THIS_MODULE,
        },
 };
 
index 48857008758cc96248b38cccc24a05754bd5197d..ca0f6b6c384b34ef327f09d77cc5be44148d4a65 100644 (file)
@@ -222,6 +222,8 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev)
        struct wm831x_isink *isink = platform_get_drvdata(pdev);
        struct wm831x *wm831x = isink->wm831x;
 
+       platform_set_drvdata(pdev, NULL);
+
        wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink);
 
        regulator_unregister(isink->regulator);
@@ -235,6 +237,7 @@ static struct platform_driver wm831x_isink_driver = {
        .remove = __devexit_p(wm831x_isink_remove),
        .driver         = {
                .name   = "wm831x-isink",
+               .owner  = THIS_MODULE,
        },
 };
 
index 61e02ac2fda334f9e17dd145cf65d938f816548e..d2406c1519a195a16437561bd3e2fadde4859f9b 100644 (file)
@@ -371,6 +371,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
        struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
        struct wm831x *wm831x = ldo->wm831x;
 
+       platform_set_drvdata(pdev, NULL);
+
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo);
        regulator_unregister(ldo->regulator);
        kfree(ldo);
@@ -383,6 +385,7 @@ static struct platform_driver wm831x_gp_ldo_driver = {
        .remove = __devexit_p(wm831x_gp_ldo_remove),
        .driver         = {
                .name   = "wm831x-ldo",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -640,6 +643,7 @@ static struct platform_driver wm831x_aldo_driver = {
        .remove = __devexit_p(wm831x_aldo_remove),
        .driver         = {
                .name   = "wm831x-aldo",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -811,6 +815,7 @@ static struct platform_driver wm831x_alive_ldo_driver = {
        .remove = __devexit_p(wm831x_alive_ldo_remove),
        .driver         = {
                .name   = "wm831x-alive-ldo",
+               .owner  = THIS_MODULE,
        },
 };
 
index e7b89e704af671aeabe8ff193022af8048fbd0bc..94227dd6ba7ba6448f04a901f2e02f9b5666eb97 100644 (file)
@@ -290,6 +290,51 @@ static int wm8350_isink_is_enabled(struct regulator_dev *rdev)
        return -EINVAL;
 }
 
+static int wm8350_isink_enable_time(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int isink = rdev_get_id(rdev);
+       int reg;
+
+       switch (isink) {
+       case WM8350_ISINK_A:
+               reg = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL);
+               break;
+       case WM8350_ISINK_B:
+               reg = wm8350_reg_read(wm8350, WM8350_CSB_FLASH_CONTROL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (reg & WM8350_CS1_FLASH_MODE) {
+               switch (reg & WM8350_CS1_ON_RAMP_MASK) {
+               case 0:
+                       return 0;
+               case 1:
+                       return 1950;
+               case 2:
+                       return 3910;
+               case 3:
+                       return 7800;
+               }
+       } else {
+               switch (reg & WM8350_CS1_ON_RAMP_MASK) {
+               case 0:
+                       return 0;
+               case 1:
+                       return 250000;
+               case 2:
+                       return 500000;
+               case 3:
+                       return 1000000;
+               }
+       }
+
+       return -EINVAL;
+}
+
+
 int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode,
                           u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp,
                           u16 drive)
@@ -1221,6 +1266,7 @@ static struct regulator_ops wm8350_isink_ops = {
        .enable = wm8350_isink_enable,
        .disable = wm8350_isink_disable,
        .is_enabled = wm8350_isink_is_enabled,
+       .enable_time = wm8350_isink_enable_time,
 };
 
 static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = {
index d9a2c988c6e7b4b8d4369713a6bf05107536da3a..924c7eb29ee99b5645c1af65e079d9267ba87803 100644 (file)
@@ -317,14 +317,17 @@ static struct regulator_desc regulators[] = {
 
 static int __devinit wm8400_regulator_probe(struct platform_device *pdev)
 {
+       struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]);
        struct regulator_dev *rdev;
 
        rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
-               pdev->dev.platform_data, dev_get_drvdata(&pdev->dev));
+                                 pdev->dev.platform_data, wm8400);
 
        if (IS_ERR(rdev))
                return PTR_ERR(rdev);
 
+       platform_set_drvdata(pdev, rdev);
+
        return 0;
 }
 
@@ -332,6 +335,7 @@ static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
 {
        struct regulator_dev *rdev = platform_get_drvdata(pdev);
 
+       platform_set_drvdata(pdev, NULL);
        regulator_unregister(rdev);
 
        return 0;
@@ -370,7 +374,6 @@ int wm8400_register_regulator(struct device *dev, int reg,
        wm8400->regulators[reg].id = reg;
        wm8400->regulators[reg].dev.parent = dev;
        wm8400->regulators[reg].dev.platform_data = initdata;
-       dev_set_drvdata(&wm8400->regulators[reg].dev, wm8400);
 
        return platform_device_register(&wm8400->regulators[reg]);
 }
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
new file mode 100644 (file)
index 0000000..95454a4
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * wm8994-regulator.c  --  Regulator driver for the WM8994
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/gpio.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+
+struct wm8994_ldo {
+       int enable;
+       bool is_enabled;
+       struct regulator_dev *regulator;
+       struct wm8994 *wm8994;
+};
+
+#define WM8994_LDO1_MAX_SELECTOR 0x7
+#define WM8994_LDO2_MAX_SELECTOR 0x3
+
+static int wm8994_ldo_enable(struct regulator_dev *rdev)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+
+       /* If we have no soft control assume that the LDO is always enabled. */
+       if (!ldo->enable)
+               return 0;
+
+       gpio_set_value(ldo->enable, 1);
+       ldo->is_enabled = true;
+
+       return 0;
+}
+
+static int wm8994_ldo_disable(struct regulator_dev *rdev)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+
+       /* If we have no soft control assume that the LDO is always enabled. */
+       if (!ldo->enable)
+               return -EINVAL;
+
+       gpio_set_value(ldo->enable, 0);
+       ldo->is_enabled = false;
+
+       return 0;
+}
+
+static int wm8994_ldo_is_enabled(struct regulator_dev *rdev)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+
+       return ldo->is_enabled;
+}
+
+static int wm8994_ldo_enable_time(struct regulator_dev *rdev)
+{
+       /* 3ms is fairly conservative but this shouldn't be too performance
+        * critical; can be tweaked per-system if required. */
+       return 3000;
+}
+
+static int wm8994_ldo1_list_voltage(struct regulator_dev *rdev,
+                                   unsigned int selector)
+{
+       if (selector > WM8994_LDO1_MAX_SELECTOR)
+               return -EINVAL;
+
+       return (selector * 100000) + 2400000;
+}
+
+static int wm8994_ldo1_get_voltage(struct regulator_dev *rdev)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+       int val;
+
+       val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_1);
+       if (val < 0)
+               return val;
+
+       val = (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT;
+
+       return wm8994_ldo1_list_voltage(rdev, val);
+}
+
+static int wm8994_ldo1_set_voltage(struct regulator_dev *rdev,
+                                  int min_uV, int max_uV)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+       int selector, v;
+
+       selector = (min_uV - 2400000) / 100000;
+       v = wm8994_ldo1_list_voltage(rdev, selector);
+       if (v < 0 || v > max_uV)
+               return -EINVAL;
+
+       selector <<= WM8994_LDO1_VSEL_SHIFT;
+
+       return wm8994_set_bits(ldo->wm8994, WM8994_LDO_1,
+                              WM8994_LDO1_VSEL_MASK, selector);
+}
+
+static struct regulator_ops wm8994_ldo1_ops = {
+       .enable = wm8994_ldo_enable,
+       .disable = wm8994_ldo_disable,
+       .is_enabled = wm8994_ldo_is_enabled,
+       .enable_time = wm8994_ldo_enable_time,
+
+       .list_voltage = wm8994_ldo1_list_voltage,
+       .get_voltage = wm8994_ldo1_get_voltage,
+       .set_voltage = wm8994_ldo1_set_voltage,
+};
+
+static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev,
+                                   unsigned int selector)
+{
+       if (selector > WM8994_LDO2_MAX_SELECTOR)
+               return -EINVAL;
+
+       return (selector * 100000) + 900000;
+}
+
+static int wm8994_ldo2_get_voltage(struct regulator_dev *rdev)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+       int val;
+
+       val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_2);
+       if (val < 0)
+               return val;
+
+       val = (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT;
+
+       return wm8994_ldo2_list_voltage(rdev, val);
+}
+
+static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev,
+                                  int min_uV, int max_uV)
+{
+       struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
+       int selector, v;
+
+       selector = (min_uV - 900000) / 100000;
+       v = wm8994_ldo2_list_voltage(rdev, selector);
+       if (v < 0 || v > max_uV)
+               return -EINVAL;
+
+       selector <<= WM8994_LDO2_VSEL_SHIFT;
+
+       return wm8994_set_bits(ldo->wm8994, WM8994_LDO_2,
+                              WM8994_LDO2_VSEL_MASK, selector);
+}
+
+static struct regulator_ops wm8994_ldo2_ops = {
+       .enable = wm8994_ldo_enable,
+       .disable = wm8994_ldo_disable,
+       .is_enabled = wm8994_ldo_is_enabled,
+       .enable_time = wm8994_ldo_enable_time,
+
+       .list_voltage = wm8994_ldo2_list_voltage,
+       .get_voltage = wm8994_ldo2_get_voltage,
+       .set_voltage = wm8994_ldo2_set_voltage,
+};
+
+static struct regulator_desc wm8994_ldo_desc[] = {
+       {
+               .name = "LDO1",
+               .id = 1,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = WM8994_LDO1_MAX_SELECTOR + 1,
+               .ops = &wm8994_ldo1_ops,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO2",
+               .id = 2,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = WM8994_LDO2_MAX_SELECTOR + 1,
+               .ops = &wm8994_ldo2_ops,
+               .owner = THIS_MODULE,
+       },
+};
+
+static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
+{
+       struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
+       struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+       int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+       struct wm8994_ldo *ldo;
+       int ret;
+
+       dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
+
+       if (!pdata)
+               return -ENODEV;
+
+       ldo = kzalloc(sizeof(struct wm8994_ldo), GFP_KERNEL);
+       if (ldo == NULL) {
+               dev_err(&pdev->dev, "Unable to allocate private data\n");
+               return -ENOMEM;
+       }
+
+       ldo->wm8994 = wm8994;
+
+       ldo->is_enabled = true;
+
+       if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) {
+               ldo->enable = pdata->ldo[id].enable;
+
+               ret = gpio_request(ldo->enable, "WM8994 LDO enable");
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n",
+                               ret);
+                       goto err;
+               }
+
+               ret = gpio_direction_output(ldo->enable, ldo->is_enabled);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "Failed to set GPIO up: %d\n",
+                               ret);
+                       goto err_gpio;
+               }
+       }
+
+       ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev,
+                                            pdata->ldo[id].init_data, ldo);
+       if (IS_ERR(ldo->regulator)) {
+               ret = PTR_ERR(ldo->regulator);
+               dev_err(wm8994->dev, "Failed to register LDO%d: %d\n",
+                       id + 1, ret);
+               goto err_gpio;
+       }
+
+       platform_set_drvdata(pdev, ldo);
+
+       return 0;
+
+err_gpio:
+       if (gpio_is_valid(ldo->enable))
+               gpio_free(ldo->enable);
+err:
+       kfree(ldo);
+       return ret;
+}
+
+static __devexit int wm8994_ldo_remove(struct platform_device *pdev)
+{
+       struct wm8994_ldo *ldo = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       regulator_unregister(ldo->regulator);
+       if (gpio_is_valid(ldo->enable))
+               gpio_free(ldo->enable);
+       kfree(ldo);
+
+       return 0;
+}
+
+static struct platform_driver wm8994_ldo_driver = {
+       .probe = wm8994_ldo_probe,
+       .remove = __devexit_p(wm8994_ldo_remove),
+       .driver         = {
+               .name   = "wm8994-ldo",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init wm8994_ldo_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&wm8994_ldo_driver);
+       if (ret != 0)
+               pr_err("Failed to register Wm8994 GP LDO driver: %d\n", ret);
+
+       return ret;
+}
+subsys_initcall(wm8994_ldo_init);
+
+static void __exit wm8994_ldo_exit(void)
+{
+       platform_driver_unregister(&wm8994_ldo_driver);
+}
+module_exit(wm8994_ldo_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8994 LDO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8994-ldo");
index be5a6b73e601c6be30966e9191c0d5315fd3d5db..40845c7e93221c7a13a236619ab8225e9638e9ca 100644 (file)
@@ -226,6 +226,7 @@ static void __exit rtc_exit(void)
 {
        rtc_dev_exit();
        class_destroy(rtc_class);
+       idr_destroy(&rtc_idr);
 }
 
 subsys_initcall(rtc_init);
index 86c61f1435155a2707ea40a90b1889d4a5374ae8..78a018b5c941c5acf51184a46189e9aedbfdff80 100644 (file)
@@ -161,7 +161,7 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
        if (offset == 0)
                return -EILSEQ;
 
-       memset(alrm, 0, sizeof(alrm));
+       memset(alrm, 0, sizeof(*alrm));
        if (alarm != ALARM_DISABLED && offset != 0) {
                rtc_time_to_tm(offset + alarm, tm);
 
index 03ea530981d1e66328625fb26567cc40e35f9f23..44c4399ee7144fc2e8cffca50d8a96426b05a8fb 100644 (file)
@@ -271,12 +271,13 @@ static int coh901331_resume(struct platform_device *pdev)
 {
        struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev);
 
-       if (device_may_wakeup(&pdev->dev))
+       if (device_may_wakeup(&pdev->dev)) {
                disable_irq_wake(rtap->irq);
-       else
+       } else {
                clk_enable(rtap->clk);
                writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK);
                clk_disable(rtap->clk);
+       }
        return 0;
 }
 #else
index 9da02d108b73370d79f45dea06f016143b93ac90..91bde976bc0fb79031f18faf5756069f04c38ac6 100644 (file)
@@ -115,6 +115,15 @@ static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
 }
 static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
 
+static struct attribute *ep93xx_rtc_attrs[] = {
+       &dev_attr_comp_preload.attr,
+       &dev_attr_comp_delete.attr,
+       NULL
+};
+
+static const struct attribute_group ep93xx_rtc_sysfs_files = {
+       .attrs  = ep93xx_rtc_attrs,
+};
 
 static int __init ep93xx_rtc_probe(struct platform_device *pdev)
 {
@@ -123,27 +132,22 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
        struct rtc_device *rtc;
        int err;
 
-       ep93xx_rtc = kzalloc(sizeof(struct ep93xx_rtc), GFP_KERNEL);
-       if (ep93xx_rtc == NULL)
+       ep93xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_rtc), GFP_KERNEL);
+       if (!ep93xx_rtc)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res == NULL) {
-               err = -ENXIO;
-               goto fail_free;
-       }
+       if (!res)
+               return -ENXIO;
 
-       res = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (res == NULL) {
-               err = -EBUSY;
-               goto fail_free;
-       }
+       if (!devm_request_mem_region(&pdev->dev, res->start,
+                                    resource_size(res), pdev->name))
+               return -EBUSY;
 
-       ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res));
-       if (ep93xx_rtc->mmio_base == NULL) {
-               err = -ENXIO;
-               goto fail;
-       }
+       ep93xx_rtc->mmio_base = devm_ioremap(&pdev->dev, res->start,
+                                            resource_size(res));
+       if (!ep93xx_rtc->mmio_base)
+               return -ENXIO;
 
        pdev->dev.platform_data = ep93xx_rtc;
 
@@ -151,53 +155,34 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
                                &pdev->dev, &ep93xx_rtc_ops, THIS_MODULE);
        if (IS_ERR(rtc)) {
                err = PTR_ERR(rtc);
-               goto fail;
+               goto exit;
        }
 
        platform_set_drvdata(pdev, rtc);
 
-       err = device_create_file(&pdev->dev, &dev_attr_comp_preload);
+       err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
        if (err)
                goto fail;
-       err = device_create_file(&pdev->dev, &dev_attr_comp_delete);
-       if (err) {
-               device_remove_file(&pdev->dev, &dev_attr_comp_preload);
-               goto fail;
-       }
 
        return 0;
 
 fail:
-       if (ep93xx_rtc->mmio_base) {
-               iounmap(ep93xx_rtc->mmio_base);
-               pdev->dev.platform_data = NULL;
-       }
-       release_mem_region(res->start, resource_size(res));
-fail_free:
-       kfree(ep93xx_rtc);
+       platform_set_drvdata(pdev, NULL);
+       rtc_device_unregister(rtc);
+exit:
+       pdev->dev.platform_data = NULL;
        return err;
 }
 
 static int __exit ep93xx_rtc_remove(struct platform_device *pdev)
 {
        struct rtc_device *rtc = platform_get_drvdata(pdev);
-       struct ep93xx_rtc *ep93xx_rtc = pdev->dev.platform_data;
-       struct resource *res;
-
-       /* cleanup sysfs */
-       device_remove_file(&pdev->dev, &dev_attr_comp_delete);
-       device_remove_file(&pdev->dev, &dev_attr_comp_preload);
 
+       sysfs_remove_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
+       platform_set_drvdata(pdev, NULL);
        rtc_device_unregister(rtc);
-
-       iounmap(ep93xx_rtc->mmio_base);
        pdev->dev.platform_data = NULL;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, resource_size(res));
-
-       platform_set_drvdata(pdev, NULL);
-
        return 0;
 }
 
index 850f983c039c52a7f6b7e60393e93bc8606bdb22..d60c81b7b693c9c26afa236cf85abafd1b6e2ec2 100644 (file)
@@ -28,6 +28,34 @@ struct mc13783_rtc {
        int valid;
 };
 
+static int mc13783_rtc_irq_enable_unlocked(struct device *dev,
+               unsigned int enabled, int irq)
+{
+       struct mc13783_rtc *priv = dev_get_drvdata(dev);
+       int (*func)(struct mc13783 *mc13783, int irq);
+
+       if (!priv->valid)
+               return -ENODATA;
+
+       func = enabled ? mc13783_irq_unmask : mc13783_irq_mask;
+       return func(priv->mc13783, irq);
+}
+
+static int mc13783_rtc_irq_enable(struct device *dev,
+               unsigned int enabled, int irq)
+{
+       struct mc13783_rtc *priv = dev_get_drvdata(dev);
+       int ret;
+
+       mc13783_lock(priv->mc13783);
+
+       ret = mc13783_rtc_irq_enable_unlocked(dev, enabled, irq);
+
+       mc13783_unlock(priv->mc13783);
+
+       return ret;
+}
+
 static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
        struct mc13783_rtc *priv = dev_get_drvdata(dev);
@@ -78,6 +106,7 @@ static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
 {
        struct mc13783_rtc *priv = dev_get_drvdata(dev);
        unsigned int seconds, days;
+       unsigned int alarmseconds;
        int ret;
 
        seconds = secs % 86400;
@@ -86,7 +115,22 @@ static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
        mc13783_lock(priv->mc13783);
 
        /*
-        * first write seconds=0 to prevent a day switch between writing days
+        * temporarily invalidate alarm to prevent triggering it when the day is
+        * already updated while the time isn't yet.
+        */
+       ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &alarmseconds);
+       if (unlikely(ret))
+               goto out;
+
+       if (alarmseconds < 86400) {
+               ret = mc13783_reg_write(priv->mc13783,
+                               MC13783_RTCTODA, 0x1ffff);
+               if (unlikely(ret))
+                       goto out;
+       }
+
+       /*
+        * write seconds=0 to prevent a day switch between writing days
         * and seconds below
         */
        ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
@@ -101,11 +145,19 @@ static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
        if (unlikely(ret))
                goto out;
 
-       ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
+       /* restore alarm */
+       if (alarmseconds < 86400) {
+               ret = mc13783_reg_write(priv->mc13783,
+                               MC13783_RTCTODA, alarmseconds);
+               if (unlikely(ret))
+                       goto out;
+       }
+
+       ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_RTCRST);
        if (unlikely(ret))
                goto out;
 
-       ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
+       ret = mc13783_irq_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
 out:
        priv->valid = !ret;
 
@@ -114,41 +166,139 @@ out:
        return ret;
 }
 
-static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
+static int mc13783_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
-       struct mc13783_rtc *priv = dev;
-       struct mc13783 *mc13783 = priv->mc13783;
+       struct mc13783_rtc *priv = dev_get_drvdata(dev);
+       unsigned seconds, days;
+       unsigned long s1970;
+       int enabled, pending;
+       int ret;
 
-       dev_dbg(&priv->rtc->dev, "1HZ\n");
+       mc13783_lock(priv->mc13783);
 
-       rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+       ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTODA, &seconds);
+       if (unlikely(ret))
+               goto out;
+       if (seconds >= 86400) {
+               ret = -ENODATA;
+               goto out;
+       }
+
+       ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days);
+       if (unlikely(ret))
+               goto out;
 
-       mc13783_ackirq(mc13783, irq);
+       ret = mc13783_irq_status(priv->mc13783, MC13783_IRQ_TODA,
+                       &enabled, &pending);
 
-       return IRQ_HANDLED;
+out:
+       mc13783_unlock(priv->mc13783);
+
+       if (ret)
+               return ret;
+
+       alarm->enabled = enabled;
+       alarm->pending = pending;
+
+       s1970 = days * 86400 + seconds;
+
+       rtc_time_to_tm(s1970, &alarm->time);
+       dev_dbg(dev, "%s: %lu\n", __func__, s1970);
+
+       return 0;
 }
 
-static int mc13783_rtc_update_irq_enable(struct device *dev,
-               unsigned int enabled)
+static int mc13783_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
        struct mc13783_rtc *priv = dev_get_drvdata(dev);
-       int ret = -ENODATA;
+       unsigned long s1970;
+       unsigned seconds, days;
+       int ret;
 
        mc13783_lock(priv->mc13783);
-       if (!priv->valid)
+
+       /* disable alarm to prevent false triggering */
+       ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, 0x1ffff);
+       if (unlikely(ret))
                goto out;
 
-       ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
-                       MC13783_IRQ_1HZ);
+       ret = mc13783_irq_ack(priv->mc13783, MC13783_IRQ_TODA);
+       if (unlikely(ret))
+               goto out;
+
+       ret = rtc_tm_to_time(&alarm->time, &s1970);
+       if (unlikely(ret))
+               goto out;
+
+       dev_dbg(dev, "%s: o%2.s %lu\n", __func__, alarm->enabled ? "n" : "ff",
+                       s1970);
+
+       ret = mc13783_rtc_irq_enable_unlocked(dev, alarm->enabled,
+                       MC13783_IRQ_TODA);
+       if (unlikely(ret))
+               goto out;
+
+       seconds = s1970 % 86400;
+       days = s1970 / 86400;
+
+       ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAYA, days);
+       if (unlikely(ret))
+               goto out;
+
+       ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTODA, seconds);
+
 out:
        mc13783_unlock(priv->mc13783);
 
        return ret;
 }
 
+static irqreturn_t mc13783_rtc_alarm_handler(int irq, void *dev)
+{
+       struct mc13783_rtc *priv = dev;
+       struct mc13783 *mc13783 = priv->mc13783;
+
+       dev_dbg(&priv->rtc->dev, "Alarm\n");
+
+       rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
+
+       mc13783_irq_ack(mc13783, irq);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
+{
+       struct mc13783_rtc *priv = dev;
+       struct mc13783 *mc13783 = priv->mc13783;
+
+       dev_dbg(&priv->rtc->dev, "1HZ\n");
+
+       rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+       mc13783_irq_ack(mc13783, irq);
+
+       return IRQ_HANDLED;
+}
+
+static int mc13783_rtc_update_irq_enable(struct device *dev,
+               unsigned int enabled)
+{
+       return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_1HZ);
+}
+
+static int mc13783_rtc_alarm_irq_enable(struct device *dev,
+               unsigned int enabled)
+{
+       return mc13783_rtc_irq_enable(dev, enabled, MC13783_IRQ_TODA);
+}
+
 static const struct rtc_class_ops mc13783_rtc_ops = {
        .read_time = mc13783_rtc_read_time,
        .set_mmss = mc13783_rtc_set_mmss,
+       .read_alarm = mc13783_rtc_read_alarm,
+       .set_alarm = mc13783_rtc_set_alarm,
+       .alarm_irq_enable = mc13783_rtc_alarm_irq_enable,
        .update_irq_enable = mc13783_rtc_update_irq_enable,
 };
 
@@ -160,7 +310,7 @@ static irqreturn_t mc13783_rtc_reset_handler(int irq, void *dev)
        dev_dbg(&priv->rtc->dev, "RTCRST\n");
        priv->valid = 0;
 
-       mc13783_mask(mc13783, irq);
+       mc13783_irq_mask(mc13783, irq);
 
        return IRQ_HANDLED;
 }
@@ -169,6 +319,7 @@ static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
 {
        int ret;
        struct mc13783_rtc *priv;
+       int rtcrst_pending;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -177,8 +328,6 @@ static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
        priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
        platform_set_drvdata(pdev, priv);
 
-       priv->valid = 1;
-
        mc13783_lock(priv->mc13783);
 
        ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
@@ -186,33 +335,45 @@ static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
        if (ret)
                goto err_reset_irq_request;
 
+       ret = mc13783_irq_status(priv->mc13783, MC13783_IRQ_RTCRST,
+                       NULL, &rtcrst_pending);
+       if (ret)
+               goto err_reset_irq_status;
+
+       priv->valid = !rtcrst_pending;
+
        ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
                        mc13783_rtc_update_handler, DRIVER_NAME, priv);
        if (ret)
                goto err_update_irq_request;
 
-       mc13783_unlock(priv->mc13783);
+       ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_TODA,
+                       mc13783_rtc_alarm_handler, DRIVER_NAME, priv);
+       if (ret)
+               goto err_alarm_irq_request;
 
        priv->rtc = rtc_device_register(pdev->name,
                        &pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
-
        if (IS_ERR(priv->rtc)) {
                ret = PTR_ERR(priv->rtc);
 
-               mc13783_lock(priv->mc13783);
+               mc13783_irq_free(priv->mc13783, MC13783_IRQ_TODA, priv);
+err_alarm_irq_request:
 
                mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
 err_update_irq_request:
 
+err_reset_irq_status:
+
                mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
 err_reset_irq_request:
 
-               mc13783_unlock(priv->mc13783);
-
                platform_set_drvdata(pdev, NULL);
                kfree(priv);
        }
 
+       mc13783_unlock(priv->mc13783);
+
        return ret;
 }
 
@@ -220,10 +381,11 @@ static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
 {
        struct mc13783_rtc *priv = platform_get_drvdata(pdev);
 
-       rtc_device_unregister(priv->rtc);
-
        mc13783_lock(priv->mc13783);
 
+       rtc_device_unregister(priv->rtc);
+
+       mc13783_irq_free(priv->mc13783, MC13783_IRQ_TODA, priv);
        mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
        mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
 
index 6bd5072d4eb7f4056c226e7982972080fb4f18ff..8710f9415d98ce35a42757b6974065d80c784f2c 100644 (file)
@@ -396,8 +396,11 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
        pdata->ioaddr = ioremap(res->start, resource_size(res));
 
        clk = clk_get(&pdev->dev, "ckil");
-       if (IS_ERR(clk))
-               return PTR_ERR(clk);
+       if (IS_ERR(clk)) {
+               iounmap(pdata->ioaddr);
+               ret = PTR_ERR(clk);
+               goto exit_free_pdata;
+       }
 
        rate = clk_get_rate(clk);
        clk_put(clk);
index e75df9d50e27243e444a7c7c09ee6ef192eb6344..2ceb365533b2a3d8d0701753a4e54716dccefb81 100644 (file)
@@ -315,7 +315,7 @@ kfree_exit:
        return ret;
 }
 
-static int pcf2123_remove(struct spi_device *spi)
+static int __devexit pcf2123_remove(struct spi_device *spi)
 {
        struct pcf2123_plat_data *pdata = spi->dev.platform_data;
        int i;
index c6a83a2a722cbf9657b9e4f9280b482f2e6e3cbb..ed1b86828124e217ff3bfbf6483d0ab1afa7d216 100644 (file)
@@ -57,7 +57,7 @@ enum {
        REG_RTC_COMP_LSB_REG,
        REG_RTC_COMP_MSB_REG,
 };
-const static u8 twl4030_rtc_reg_map[] = {
+static const u8 twl4030_rtc_reg_map[] = {
        [REG_SECONDS_REG] = 0x00,
        [REG_MINUTES_REG] = 0x01,
        [REG_HOURS_REG] = 0x02,
@@ -80,7 +80,7 @@ const static u8 twl4030_rtc_reg_map[] = {
        [REG_RTC_COMP_LSB_REG] = 0x10,
        [REG_RTC_COMP_MSB_REG] = 0x11,
 };
-const static u8 twl6030_rtc_reg_map[] = {
+static const u8 twl6030_rtc_reg_map[] = {
        [REG_SECONDS_REG] = 0x00,
        [REG_MINUTES_REG] = 0x01,
        [REG_HOURS_REG] = 0x02,
index 746e07033dcea6a9aa2c0c0e9a53476fb02cd75c..d6ff73395623cbe97d59b803fa6528b43002c8e2 100644 (file)
@@ -1009,6 +1009,10 @@ config SERIAL_SH_SCI_CONSOLE
        depends on SERIAL_SH_SCI=y
        select SERIAL_CORE_CONSOLE
 
+config SERIAL_SH_SCI_DMA
+       bool "DMA support"
+       depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+
 config SERIAL_PNX8XXX
        bool "Enable PNX8XXX SoCs' UART Support"
        depends on MIPS && (SOC_PNX8550 || SOC_PNX833X)
index 42f3333c4ad09c9ccaf38973b3722dbb68b5e2e6..980f39449ee5635ccb7b4f681151892e92a493b0 100644 (file)
@@ -48,6 +48,9 @@
 #include <linux/ctype.h>
 #include <linux/err.h>
 #include <linux/list.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <linux/timer.h>
 
 #ifdef CONFIG_SUPERH
 #include <asm/sh_bios.h>
@@ -84,6 +87,27 @@ struct sci_port {
        struct clk              *dclk;
 
        struct list_head        node;
+       struct dma_chan                 *chan_tx;
+       struct dma_chan                 *chan_rx;
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+       struct device                   *dma_dev;
+       enum sh_dmae_slave_chan_id      slave_tx;
+       enum sh_dmae_slave_chan_id      slave_rx;
+       struct dma_async_tx_descriptor  *desc_tx;
+       struct dma_async_tx_descriptor  *desc_rx[2];
+       dma_cookie_t                    cookie_tx;
+       dma_cookie_t                    cookie_rx[2];
+       dma_cookie_t                    active_rx;
+       struct scatterlist              sg_tx;
+       unsigned int                    sg_len_tx;
+       struct scatterlist              sg_rx[2];
+       size_t                          buf_len_rx;
+       struct sh_dmae_slave            param_tx;
+       struct sh_dmae_slave            param_rx;
+       struct work_struct              work_tx;
+       struct work_struct              work_rx;
+       struct timer_list               rx_timer;
+#endif
 };
 
 struct sh_sci_priv {
@@ -269,29 +293,44 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
     defined(CONFIG_CPU_SUBTYPE_SH7780) || \
     defined(CONFIG_CPU_SUBTYPE_SH7785) || \
     defined(CONFIG_CPU_SUBTYPE_SH7786)
-static inline int scif_txroom(struct uart_port *port)
+static int scif_txfill(struct uart_port *port)
 {
-       return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff);
+       return sci_in(port, SCTFDR) & 0xff;
 }
 
-static inline int scif_rxroom(struct uart_port *port)
+static int scif_txroom(struct uart_port *port)
+{
+       return SCIF_TXROOM_MAX - scif_txfill(port);
+}
+
+static int scif_rxfill(struct uart_port *port)
 {
        return sci_in(port, SCRFDR) & 0xff;
 }
 #elif defined(CONFIG_CPU_SUBTYPE_SH7763)
-static inline int scif_txroom(struct uart_port *port)
+static int scif_txfill(struct uart_port *port)
 {
-       if ((port->mapbase == 0xffe00000) ||
-           (port->mapbase == 0xffe08000)) {
+       if (port->mapbase == 0xffe00000 ||
+           port->mapbase == 0xffe08000)
                /* SCIF0/1*/
-               return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff);
-       } else {
+               return sci_in(port, SCTFDR) & 0xff;
+       else
                /* SCIF2 */
-               return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
-       }
+               return sci_in(port, SCFDR) >> 8;
 }
 
-static inline int scif_rxroom(struct uart_port *port)
+static int scif_txroom(struct uart_port *port)
+{
+       if (port->mapbase == 0xffe00000 ||
+           port->mapbase == 0xffe08000)
+               /* SCIF0/1*/
+               return SCIF_TXROOM_MAX - scif_txfill(port);
+       else
+               /* SCIF2 */
+               return SCIF2_TXROOM_MAX - scif_txfill(port);
+}
+
+static int scif_rxfill(struct uart_port *port)
 {
        if ((port->mapbase == 0xffe00000) ||
            (port->mapbase == 0xffe08000)) {
@@ -303,23 +342,33 @@ static inline int scif_rxroom(struct uart_port *port)
        }
 }
 #else
-static inline int scif_txroom(struct uart_port *port)
+static int scif_txfill(struct uart_port *port)
+{
+       return sci_in(port, SCFDR) >> 8;
+}
+
+static int scif_txroom(struct uart_port *port)
 {
-       return SCIF_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
+       return SCIF_TXROOM_MAX - scif_txfill(port);
 }
 
-static inline int scif_rxroom(struct uart_port *port)
+static int scif_rxfill(struct uart_port *port)
 {
        return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
 }
 #endif
 
-static inline int sci_txroom(struct uart_port *port)
+static int sci_txfill(struct uart_port *port)
 {
-       return (sci_in(port, SCxSR) & SCI_TDRE) != 0;
+       return !(sci_in(port, SCxSR) & SCI_TDRE);
 }
 
-static inline int sci_rxroom(struct uart_port *port)
+static int sci_txroom(struct uart_port *port)
+{
+       return !sci_txfill(port);
+}
+
+static int sci_rxfill(struct uart_port *port)
 {
        return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
 }
@@ -406,9 +455,9 @@ static inline void sci_receive_chars(struct uart_port *port)
 
        while (1) {
                if (port->type == PORT_SCI)
-                       count = sci_rxroom(port);
+                       count = sci_rxfill(port);
                else
-                       count = scif_rxroom(port);
+                       count = scif_rxfill(port);
 
                /* Don't copy more bytes than there is room for in the buffer */
                count = tty_buffer_request_room(tty, count);
@@ -453,10 +502,10 @@ static inline void sci_receive_chars(struct uart_port *port)
                                }
 
                                /* Store data and status */
-                               if (status&SCxSR_FER(port)) {
+                               if (status & SCxSR_FER(port)) {
                                        flag = TTY_FRAME;
                                        dev_notice(port->dev, "frame error\n");
-                               } else if (status&SCxSR_PER(port)) {
+                               } else if (status & SCxSR_PER(port)) {
                                        flag = TTY_PARITY;
                                        dev_notice(port->dev, "parity error\n");
                                } else
@@ -618,13 +667,39 @@ static inline int sci_handle_breaks(struct uart_port *port)
        return copied;
 }
 
-static irqreturn_t sci_rx_interrupt(int irq, void *port)
+static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
 {
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+       struct uart_port *port = ptr;
+       struct sci_port *s = to_sci_port(port);
+
+       if (s->chan_rx) {
+               unsigned long tout;
+               u16 scr = sci_in(port, SCSCR);
+               u16 ssr = sci_in(port, SCxSR);
+
+               /* Disable future Rx interrupts */
+               sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE);
+               /* Clear current interrupt */
+               sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port)));
+               /* Calculate delay for 1.5 DMA buffers */
+               tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 /
+                       port->fifosize / 2;
+               dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n",
+                       tout * 1000 / HZ);
+               if (tout < 2)
+                       tout = 2;
+               mod_timer(&s->rx_timer, jiffies + tout);
+
+               return IRQ_HANDLED;
+       }
+#endif
+
        /* I think sci_receive_chars has to be called irrespective
         * of whether the I_IXOFF is set, otherwise, how is the interrupt
         * to be disabled?
         */
-       sci_receive_chars(port);
+       sci_receive_chars(ptr);
 
        return IRQ_HANDLED;
 }
@@ -680,6 +755,7 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
 {
        unsigned short ssr_status, scr_status, err_enabled;
        struct uart_port *port = ptr;
+       struct sci_port *s = to_sci_port(port);
        irqreturn_t ret = IRQ_NONE;
 
        ssr_status = sci_in(port, SCxSR);
@@ -687,10 +763,15 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
        err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE);
 
        /* Tx Interrupt */
-       if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE))
+       if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) &&
+           !s->chan_tx)
                ret = sci_tx_interrupt(irq, ptr);
-       /* Rx Interrupt */
-       if ((ssr_status & SCxSR_RDxF(port)) && (scr_status & SCI_CTRL_FLAGS_RIE))
+       /*
+        * Rx Interrupt: if we're using DMA, the DMA controller clears RDF /
+        * DR flags
+        */
+       if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) &&
+           (scr_status & SCI_CTRL_FLAGS_RIE))
                ret = sci_rx_interrupt(irq, ptr);
        /* Error Interrupt */
        if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled)
@@ -699,6 +780,10 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
        if ((ssr_status & SCxSR_BRK(port)) && err_enabled)
                ret = sci_br_interrupt(irq, ptr);
 
+       WARN_ONCE(ret == IRQ_NONE,
+                 "%s: %d IRQ %d, status %x, control %x\n", __func__,
+                 irq, port->line, ssr_status, scr_status);
+
        return ret;
 }
 
@@ -800,7 +885,9 @@ static void sci_free_irq(struct sci_port *port)
 static unsigned int sci_tx_empty(struct uart_port *port)
 {
        unsigned short status = sci_in(port, SCxSR);
-       return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0;
+       unsigned short in_tx_fifo = scif_txfill(port);
+
+       return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
 }
 
 static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -812,16 +899,297 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
 
 static unsigned int sci_get_mctrl(struct uart_port *port)
 {
-       /* This routine is used for geting signals of: DTR, DCD, DSR, RI,
+       /* This routine is used for getting signals of: DTR, DCD, DSR, RI,
           and CTS/RTS */
 
        return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR;
 }
 
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+static void sci_dma_tx_complete(void *arg)
+{
+       struct sci_port *s = arg;
+       struct uart_port *port = &s->port;
+       struct circ_buf *xmit = &port->state->xmit;
+       unsigned long flags;
+
+       dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       xmit->tail += s->sg_tx.length;
+       xmit->tail &= UART_XMIT_SIZE - 1;
+
+       port->icount.tx += s->sg_tx.length;
+
+       async_tx_ack(s->desc_tx);
+       s->cookie_tx = -EINVAL;
+       s->desc_tx = NULL;
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       if (uart_circ_chars_pending(xmit))
+               schedule_work(&s->work_tx);
+}
+
+/* Locking: called with port lock held */
+static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty,
+                          size_t count)
+{
+       struct uart_port *port = &s->port;
+       int i, active, room;
+
+       room = tty_buffer_request_room(tty, count);
+
+       if (s->active_rx == s->cookie_rx[0]) {
+               active = 0;
+       } else if (s->active_rx == s->cookie_rx[1]) {
+               active = 1;
+       } else {
+               dev_err(port->dev, "cookie %d not found!\n", s->active_rx);
+               return 0;
+       }
+
+       if (room < count)
+               dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
+                        count - room);
+       if (!room)
+               return room;
+
+       for (i = 0; i < room; i++)
+               tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i],
+                                    TTY_NORMAL);
+
+       port->icount.rx += room;
+
+       return room;
+}
+
+static void sci_dma_rx_complete(void *arg)
+{
+       struct sci_port *s = arg;
+       struct uart_port *port = &s->port;
+       struct tty_struct *tty = port->state->port.tty;
+       unsigned long flags;
+       int count;
+
+       dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       count = sci_dma_rx_push(s, tty, s->buf_len_rx);
+
+       mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5));
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (count)
+               tty_flip_buffer_push(tty);
+
+       schedule_work(&s->work_rx);
+}
+
+static void sci_start_rx(struct uart_port *port);
+static void sci_start_tx(struct uart_port *port);
+
+static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
+{
+       struct dma_chan *chan = s->chan_rx;
+       struct uart_port *port = &s->port;
+
+       s->chan_rx = NULL;
+       s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL;
+       dma_release_channel(chan);
+       dma_free_coherent(port->dev, s->buf_len_rx * 2,
+                         sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0]));
+       if (enable_pio)
+               sci_start_rx(port);
+}
+
+static void sci_tx_dma_release(struct sci_port *s, bool enable_pio)
+{
+       struct dma_chan *chan = s->chan_tx;
+       struct uart_port *port = &s->port;
+
+       s->chan_tx = NULL;
+       s->cookie_tx = -EINVAL;
+       dma_release_channel(chan);
+       if (enable_pio)
+               sci_start_tx(port);
+}
+
+static void sci_submit_rx(struct sci_port *s)
+{
+       struct dma_chan *chan = s->chan_rx;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               struct scatterlist *sg = &s->sg_rx[i];
+               struct dma_async_tx_descriptor *desc;
+
+               desc = chan->device->device_prep_slave_sg(chan,
+                       sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT);
+
+               if (desc) {
+                       s->desc_rx[i] = desc;
+                       desc->callback = sci_dma_rx_complete;
+                       desc->callback_param = s;
+                       s->cookie_rx[i] = desc->tx_submit(desc);
+               }
+
+               if (!desc || s->cookie_rx[i] < 0) {
+                       if (i) {
+                               async_tx_ack(s->desc_rx[0]);
+                               s->cookie_rx[0] = -EINVAL;
+                       }
+                       if (desc) {
+                               async_tx_ack(desc);
+                               s->cookie_rx[i] = -EINVAL;
+                       }
+                       dev_warn(s->port.dev,
+                                "failed to re-start DMA, using PIO\n");
+                       sci_rx_dma_release(s, true);
+                       return;
+               }
+       }
+
+       s->active_rx = s->cookie_rx[0];
+
+       dma_async_issue_pending(chan);
+}
+
+static void work_fn_rx(struct work_struct *work)
+{
+       struct sci_port *s = container_of(work, struct sci_port, work_rx);
+       struct uart_port *port = &s->port;
+       struct dma_async_tx_descriptor *desc;
+       int new;
+
+       if (s->active_rx == s->cookie_rx[0]) {
+               new = 0;
+       } else if (s->active_rx == s->cookie_rx[1]) {
+               new = 1;
+       } else {
+               dev_err(port->dev, "cookie %d not found!\n", s->active_rx);
+               return;
+       }
+       desc = s->desc_rx[new];
+
+       if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) !=
+           DMA_SUCCESS) {
+               /* Handle incomplete DMA receive */
+               struct tty_struct *tty = port->state->port.tty;
+               struct dma_chan *chan = s->chan_rx;
+               struct sh_desc *sh_desc = container_of(desc, struct sh_desc,
+                                                      async_tx);
+               unsigned long flags;
+               int count;
+
+               chan->device->device_terminate_all(chan);
+               dev_dbg(port->dev, "Read %u bytes with cookie %d\n",
+                       sh_desc->partial, sh_desc->cookie);
+
+               spin_lock_irqsave(&port->lock, flags);
+               count = sci_dma_rx_push(s, tty, sh_desc->partial);
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               if (count)
+                       tty_flip_buffer_push(tty);
+
+               sci_submit_rx(s);
+
+               return;
+       }
+
+       s->cookie_rx[new] = desc->tx_submit(desc);
+       if (s->cookie_rx[new] < 0) {
+               dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
+               sci_rx_dma_release(s, true);
+               return;
+       }
+
+       dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__,
+               s->cookie_rx[new], new);
+
+       s->active_rx = s->cookie_rx[!new];
+}
+
+static void work_fn_tx(struct work_struct *work)
+{
+       struct sci_port *s = container_of(work, struct sci_port, work_tx);
+       struct dma_async_tx_descriptor *desc;
+       struct dma_chan *chan = s->chan_tx;
+       struct uart_port *port = &s->port;
+       struct circ_buf *xmit = &port->state->xmit;
+       struct scatterlist *sg = &s->sg_tx;
+
+       /*
+        * DMA is idle now.
+        * Port xmit buffer is already mapped, and it is one page... Just adjust
+        * offsets and lengths. Since it is a circular buffer, we have to
+        * transmit till the end, and then the rest. Take the port lock to get a
+        * consistent xmit buffer state.
+        */
+       spin_lock_irq(&port->lock);
+       sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
+       sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) +
+               sg->offset;
+       sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE),
+               CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE));
+       sg->dma_length = sg->length;
+       spin_unlock_irq(&port->lock);
+
+       BUG_ON(!sg->length);
+
+       desc = chan->device->device_prep_slave_sg(chan,
+                       sg, s->sg_len_tx, DMA_TO_DEVICE,
+                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!desc) {
+               /* switch to PIO */
+               sci_tx_dma_release(s, true);
+               return;
+       }
+
+       dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
+
+       spin_lock_irq(&port->lock);
+       s->desc_tx = desc;
+       desc->callback = sci_dma_tx_complete;
+       desc->callback_param = s;
+       spin_unlock_irq(&port->lock);
+       s->cookie_tx = desc->tx_submit(desc);
+       if (s->cookie_tx < 0) {
+               dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n");
+               /* switch to PIO */
+               sci_tx_dma_release(s, true);
+               return;
+       }
+
+       dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__,
+               xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
+
+       dma_async_issue_pending(chan);
+}
+#endif
+
 static void sci_start_tx(struct uart_port *port)
 {
        unsigned short ctrl;
 
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+       struct sci_port *s = to_sci_port(port);
+
+       if (s->chan_tx) {
+               if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0)
+                       schedule_work(&s->work_tx);
+
+               return;
+       }
+#endif
+
        /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
        ctrl = sci_in(port, SCSCR);
        ctrl |= SCI_CTRL_FLAGS_TIE;
@@ -838,13 +1206,12 @@ static void sci_stop_tx(struct uart_port *port)
        sci_out(port, SCSCR, ctrl);
 }
 
-static void sci_start_rx(struct uart_port *port, unsigned int tty_start)
+static void sci_start_rx(struct uart_port *port)
 {
-       unsigned short ctrl;
+       unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE;
 
        /* Set RIE (Receive Interrupt Enable) bit in SCSCR */
-       ctrl = sci_in(port, SCSCR);
-       ctrl |= SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE;
+       ctrl |= sci_in(port, SCSCR);
        sci_out(port, SCSCR, ctrl);
 }
 
@@ -868,16 +1235,154 @@ static void sci_break_ctl(struct uart_port *port, int break_state)
        /* Nothing here yet .. */
 }
 
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+static bool filter(struct dma_chan *chan, void *slave)
+{
+       struct sh_dmae_slave *param = slave;
+
+       dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__,
+               param->slave_id);
+
+       if (param->dma_dev == chan->device->dev) {
+               chan->private = param;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+static void rx_timer_fn(unsigned long arg)
+{
+       struct sci_port *s = (struct sci_port *)arg;
+       struct uart_port *port = &s->port;
+
+       u16 scr = sci_in(port, SCSCR);
+       sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE);
+       dev_dbg(port->dev, "DMA Rx timed out\n");
+       schedule_work(&s->work_rx);
+}
+
+static void sci_request_dma(struct uart_port *port)
+{
+       struct sci_port *s = to_sci_port(port);
+       struct sh_dmae_slave *param;
+       struct dma_chan *chan;
+       dma_cap_mask_t mask;
+       int nent;
+
+       dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__,
+               port->line, s->dma_dev);
+
+       if (!s->dma_dev)
+               return;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       param = &s->param_tx;
+
+       /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */
+       param->slave_id = s->slave_tx;
+       param->dma_dev = s->dma_dev;
+
+       s->cookie_tx = -EINVAL;
+       chan = dma_request_channel(mask, filter, param);
+       dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan);
+       if (chan) {
+               s->chan_tx = chan;
+               sg_init_table(&s->sg_tx, 1);
+               /* UART circular tx buffer is an aligned page. */
+               BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+               sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf),
+                           UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK);
+               nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE);
+               if (!nent)
+                       sci_tx_dma_release(s, false);
+               else
+                       dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+                               sg_dma_len(&s->sg_tx),
+                               port->state->xmit.buf, sg_dma_address(&s->sg_tx));
+
+               s->sg_len_tx = nent;
+
+               INIT_WORK(&s->work_tx, work_fn_tx);
+       }
+
+       param = &s->param_rx;
+
+       /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */
+       param->slave_id = s->slave_rx;
+       param->dma_dev = s->dma_dev;
+
+       chan = dma_request_channel(mask, filter, param);
+       dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
+       if (chan) {
+               dma_addr_t dma[2];
+               void *buf[2];
+               int i;
+
+               s->chan_rx = chan;
+
+               s->buf_len_rx = 2 * max(16, (int)port->fifosize);
+               buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2,
+                                           &dma[0], GFP_KERNEL);
+
+               if (!buf[0]) {
+                       dev_warn(port->dev,
+                                "failed to allocate dma buffer, using PIO\n");
+                       sci_rx_dma_release(s, true);
+                       return;
+               }
+
+               buf[1] = buf[0] + s->buf_len_rx;
+               dma[1] = dma[0] + s->buf_len_rx;
+
+               for (i = 0; i < 2; i++) {
+                       struct scatterlist *sg = &s->sg_rx[i];
+
+                       sg_init_table(sg, 1);
+                       sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx,
+                                   (int)buf[i] & ~PAGE_MASK);
+                       sg->dma_address = dma[i];
+                       sg->dma_length = sg->length;
+               }
+
+               INIT_WORK(&s->work_rx, work_fn_rx);
+               setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s);
+
+               sci_submit_rx(s);
+       }
+}
+
+static void sci_free_dma(struct uart_port *port)
+{
+       struct sci_port *s = to_sci_port(port);
+
+       if (!s->dma_dev)
+               return;
+
+       if (s->chan_tx)
+               sci_tx_dma_release(s, false);
+       if (s->chan_rx)
+               sci_rx_dma_release(s, false);
+}
+#endif
+
 static int sci_startup(struct uart_port *port)
 {
        struct sci_port *s = to_sci_port(port);
 
+       dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
        if (s->enable)
                s->enable(port);
 
        sci_request_irq(s);
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+       sci_request_dma(port);
+#endif
        sci_start_tx(port);
-       sci_start_rx(port, 1);
+       sci_start_rx(port);
 
        return 0;
 }
@@ -886,8 +1391,13 @@ static void sci_shutdown(struct uart_port *port)
 {
        struct sci_port *s = to_sci_port(port);
 
+       dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
        sci_stop_rx(port);
        sci_stop_tx(port);
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+       sci_free_dma(port);
+#endif
        sci_free_irq(s);
 
        if (s->disable)
@@ -937,6 +1447,9 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 
        sci_out(port, SCSMR, smr_val);
 
+       dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t,
+               SCSCR_INIT(port));
+
        if (t > 0) {
                if (t >= 256) {
                        sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1);
@@ -954,7 +1467,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
        sci_out(port, SCSCR, SCSCR_INIT(port));
 
        if ((termios->c_cflag & CREAD) != 0)
-               sci_start_rx(port, 0);
+               sci_start_rx(port);
 }
 
 static const char *sci_type(struct uart_port *port)
@@ -1049,19 +1562,21 @@ static void __devinit sci_init_single(struct platform_device *dev,
                                      unsigned int index,
                                      struct plat_sci_port *p)
 {
-       sci_port->port.ops      = &sci_uart_ops;
-       sci_port->port.iotype   = UPIO_MEM;
-       sci_port->port.line     = index;
+       struct uart_port *port = &sci_port->port;
+
+       port->ops       = &sci_uart_ops;
+       port->iotype    = UPIO_MEM;
+       port->line      = index;
 
        switch (p->type) {
        case PORT_SCIFA:
-               sci_port->port.fifosize = 64;
+               port->fifosize = 64;
                break;
        case PORT_SCIF:
-               sci_port->port.fifosize = 16;
+               port->fifosize = 16;
                break;
        default:
-               sci_port->port.fifosize = 1;
+               port->fifosize = 1;
                break;
        }
 
@@ -1070,19 +1585,28 @@ static void __devinit sci_init_single(struct platform_device *dev,
                sci_port->dclk = clk_get(&dev->dev, "peripheral_clk");
                sci_port->enable = sci_clk_enable;
                sci_port->disable = sci_clk_disable;
-               sci_port->port.dev = &dev->dev;
+               port->dev = &dev->dev;
        }
 
        sci_port->break_timer.data = (unsigned long)sci_port;
        sci_port->break_timer.function = sci_break_timer;
        init_timer(&sci_port->break_timer);
 
-       sci_port->port.mapbase  = p->mapbase;
-       sci_port->port.membase  = p->membase;
+       port->mapbase   = p->mapbase;
+       port->membase   = p->membase;
 
-       sci_port->port.irq      = p->irqs[SCIx_TXI_IRQ];
-       sci_port->port.flags    = p->flags;
-       sci_port->type          = sci_port->port.type = p->type;
+       port->irq       = p->irqs[SCIx_TXI_IRQ];
+       port->flags     = p->flags;
+       sci_port->type  = port->type = p->type;
+
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+       sci_port->dma_dev       = p->dma_dev;
+       sci_port->slave_tx      = p->dma_slave_tx;
+       sci_port->slave_rx      = p->dma_slave_rx;
+
+       dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__,
+               p->dma_dev, p->dma_slave_tx, p->dma_slave_rx);
+#endif
 
        memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs));
 }
index bf38280def3f832086bb8f1d97f5483f22f7e983..63275529ff55ebecf81a04db5d5626eee07bea5d 100644 (file)
@@ -971,7 +971,7 @@ int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr)
 
        if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
            (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
-               err = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
+               err = dquot_transfer(inode, attr);
                if (err)
                        goto err_out_exit;
        }
index c83c975152a6ae7f2e3fe09d275d4946c6c67ffb..d41811bfef2a35acc1eb52127cb3649c42efc87c 100644 (file)
@@ -117,13 +117,20 @@ static const char *format_endpt =
  * However, these will come from functions that return ptrs to each of them.
  */
 
-static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq);
-/* guarded by usbfs_mutex */
-static unsigned int conndiscevcnt;
-
-/* this struct stores the poll state for <mountpoint>/devices pollers */
-struct usb_device_status {
-       unsigned int lastev;
+/*
+ * Wait for an connect/disconnect event to happen. We initialize
+ * the event counter with an odd number, and each event will increment
+ * the event counter by two, so it will always _stay_ odd. That means
+ * that it will never be zero, so "event 0" will never match a current
+ * event, and thus 'poll' will always trigger as readable for the first
+ * time it gets called.
+ */
+static struct device_connect_event {
+       atomic_t count;
+       wait_queue_head_t wait;
+} device_event = {
+       .count = ATOMIC_INIT(1),
+       .wait = __WAIT_QUEUE_HEAD_INITIALIZER(device_event.wait)
 };
 
 struct class_info {
@@ -157,10 +164,8 @@ static const struct class_info clas_info[] =
 
 void usbfs_conn_disc_event(void)
 {
-       mutex_lock(&usbfs_mutex);
-       conndiscevcnt++;
-       mutex_unlock(&usbfs_mutex);
-       wake_up(&deviceconndiscwq);
+       atomic_add(2, &device_event.count);
+       wake_up(&device_event.wait);
 }
 
 static const char *class_decode(const int class)
@@ -632,42 +637,16 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
 static unsigned int usb_device_poll(struct file *file,
                                    struct poll_table_struct *wait)
 {
-       struct usb_device_status *st;
-       unsigned int mask = 0;
-
-       mutex_lock(&usbfs_mutex);
-       st = file->private_data;
-       if (!st) {
-               st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL);
-               if (!st) {
-                       mutex_unlock(&usbfs_mutex);
-                       return POLLIN;
-               }
-
-               st->lastev = conndiscevcnt;
-               file->private_data = st;
-               mask = POLLIN;
-       }
+       unsigned int event_count;
 
-       if (file->f_mode & FMODE_READ)
-               poll_wait(file, &deviceconndiscwq, wait);
-       if (st->lastev != conndiscevcnt)
-               mask |= POLLIN;
-       st->lastev = conndiscevcnt;
-       mutex_unlock(&usbfs_mutex);
-       return mask;
-}
+       poll_wait(file, &device_event.wait, wait);
 
-static int usb_device_open(struct inode *inode, struct file *file)
-{
-       file->private_data = NULL;
-       return 0;
-}
+       event_count = atomic_read(&device_event.count);
+       if (file->f_version != event_count) {
+               file->f_version = event_count;
+               return POLLIN | POLLRDNORM;
+       }
 
-static int usb_device_release(struct inode *inode, struct file *file)
-{
-       kfree(file->private_data);
-       file->private_data = NULL;
        return 0;
 }
 
@@ -699,6 +678,4 @@ const struct file_operations usbfs_devices_fops = {
        .llseek =       usb_device_lseek,
        .read =         usb_device_read,
        .poll =         usb_device_poll,
-       .open =         usb_device_open,
-       .release =      usb_device_release,
 };
index b1935fe156a0320769481cf6419c4d58286c52bc..5a3cdd08f1d05c7d589399670fa2ac4eec7eb4a7 100644 (file)
@@ -1050,7 +1050,7 @@ static void invalidate_sub(struct fsg_lun *curlun)
        unsigned long   rc;
 
        rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
-       VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc);
+       VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
 }
 
 static int do_verify(struct fsg_common *common)
index a90dd2db04889aa5b20fa0f938d367c8cf31c9c7..b49d86e3e45b639d2d3f4760d027e38a0fd52d44 100644 (file)
@@ -1448,7 +1448,7 @@ static void invalidate_sub(struct fsg_lun *curlun)
        unsigned long   rc;
 
        rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
-       VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc);
+       VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
 }
 
 static int do_verify(struct fsg_dev *fsg)
index fc7d9bbb548c4637e329080d2ca6110bff86cd75..8e8f18d29d7ae5258705244710d160a3107dbe23 100644 (file)
@@ -37,6 +37,7 @@ config VGACON_SOFT_SCROLLBACK
 config VGACON_SOFT_SCROLLBACK_SIZE
        int "Scrollback Buffer Size (in KB)"
        depends on VGACON_SOFT_SCROLLBACK
+       range 1 1024
        default "64"
        help
          Enter the amount of System RAM to allocate for the scrollback
index 3681c6a8821239ad58858a062e0f2e6177f994fa..b0a3fa00706d2e25896fc862aa1bcc116010a6c7 100644 (file)
@@ -3025,6 +3025,20 @@ static int fbcon_fb_unregistered(struct fb_info *info)
        return 0;
 }
 
+static void fbcon_remap_all(int idx)
+{
+       int i;
+       for (i = first_fb_vc; i <= last_fb_vc; i++)
+               set_con2fb_map(i, idx, 0);
+
+       if (con_is_bound(&fb_con)) {
+               printk(KERN_INFO "fbcon: Remapping primary device, "
+                      "fb%i, to tty %i-%i\n", idx,
+                      first_fb_vc + 1, last_fb_vc + 1);
+               info_idx = idx;
+       }
+}
+
 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
 static void fbcon_select_primary(struct fb_info *info)
 {
@@ -3225,6 +3239,10 @@ static int fbcon_event_notify(struct notifier_block *self,
                caps = event->data;
                fbcon_get_requirement(info, caps);
                break;
+       case FB_EVENT_REMAP_ALL_CONSOLE:
+               idx = info->node;
+               fbcon_remap_all(idx);
+               break;
        }
 done:
        return ret;
index cc4bbbe44aca6b4672568f5ee36d27bdc1b17222..182dd6f8aadd04e2a3502a3c28429c410cbf01dc 100644 (file)
@@ -112,7 +112,7 @@ static int          vga_video_font_height;
 static int             vga_scan_lines          __read_mostly;
 static unsigned int    vga_rolled_over;
 
-int vgacon_text_mode_force = 0;
+static int vgacon_text_mode_force;
 
 bool vgacon_text_force(void)
 {
index 99bbd282ce634186e5d28f9a13cd9ba78658ddc2..a15b44e9c003c656aa030c8242f9c2be3cf4a611 100644 (file)
@@ -1513,7 +1513,6 @@ register_framebuffer(struct fb_info *fb_info)
                                       fb_info->fix.id,
                                       registered_fb[i]->fix.id);
                                unregister_framebuffer(registered_fb[i]);
-                               break;
                        }
                }
        }
index 1b65732169984032da0093371c448cfcf14db020..625447f645d935d59cc23ee5813357ae044ae8ce 100644 (file)
@@ -649,6 +649,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
                goto out_req_regions;
 
        pci_set_drvdata(pci_dev, vp_dev);
+       pci_set_master(pci_dev);
 
        /* we use the subsystem vendor/device id as the virtio vendor/device
         * id.  this allows us to use the same PCI vendor/device id for all
index cab100acf983905b53d5c85b6ae76496d7b9b7c8..fad3df2c1276165f1e194b7c5f7bb66cda934820 100644 (file)
@@ -1,6 +1,8 @@
+menu "Xen driver support"
+       depends on XEN
+
 config XEN_BALLOON
        bool "Xen memory balloon driver"
-       depends on XEN
        default y
        help
          The balloon driver allows the Xen domain to request more memory from
@@ -20,7 +22,6 @@ config XEN_SCRUB_PAGES
 
 config XEN_DEV_EVTCHN
        tristate "Xen /dev/xen/evtchn device"
-       depends on XEN
        default y
        help
          The evtchn driver allows a userspace process to triger event
@@ -30,7 +31,6 @@ config XEN_DEV_EVTCHN
 
 config XENFS
        tristate "Xen filesystem"
-       depends on XEN
        default y
        help
          The xen filesystem provides a way for domains to share
@@ -53,11 +53,13 @@ config XEN_COMPAT_XENFS
 
 config XEN_SYS_HYPERVISOR
        bool "Create xen entries under /sys/hypervisor"
-       depends on XEN && SYSFS
+       depends on SYSFS
        select SYS_HYPERVISOR
        default y
        help
          Create entries under /sys/hypervisor describing the Xen
         hypervisor environment.  When running native or in another
         virtual environment, /sys/hypervisor will still be present,
-        but will have no xen contents.
\ No newline at end of file
+        but will have no xen contents.
+
+endmenu
index 14d9442045710632418f1060c05878d5113176f8..08b2eb157048b26aafd052daaeba4c8e38239f0a 100644 (file)
@@ -151,7 +151,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
                        if (access == V9FS_ACCESS_SINGLE)
                                return ERR_PTR(-EPERM);
 
-                       if (v9fs_extended(v9ses))
+                       if (v9fs_proto_dotu(v9ses))
                                uname = NULL;
                        else
                                uname = v9ses->uname;
index 7d6c2139891db0d04673c6078d6a3f85c446dbb6..6c7f6a2511154a3016eb1bc433f012bb1d3128f5 100644 (file)
@@ -241,7 +241,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
        list_add(&v9ses->slist, &v9fs_sessionlist);
        spin_unlock(&v9fs_sessionlist_lock);
 
-       v9ses->flags = V9FS_EXTENDED | V9FS_ACCESS_USER;
+       v9ses->flags = V9FS_PROTO_2000U | V9FS_ACCESS_USER;
        strcpy(v9ses->uname, V9FS_DEFUSER);
        strcpy(v9ses->aname, V9FS_DEFANAME);
        v9ses->uid = ~0;
@@ -262,13 +262,13 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
                goto error;
        }
 
-       if (!v9ses->clnt->dotu)
-               v9ses->flags &= ~V9FS_EXTENDED;
+       if (!p9_is_proto_dotu(v9ses->clnt))
+               v9ses->flags &= ~V9FS_PROTO_2000U;
 
        v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
 
        /* for legacy mode, fall back to V9FS_ACCESS_ANY */
-       if (!v9fs_extended(v9ses) &&
+       if (!v9fs_proto_dotu(v9ses) &&
                ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
 
                v9ses->flags &= ~V9FS_ACCESS_MASK;
index 019f4ccb70c1a14d302d8d8e945cdbd98455bb43..79000bf624919acf26700d6689ba2d6328108650 100644 (file)
@@ -23,7 +23,8 @@
 
 /**
  * enum p9_session_flags - option flags for each 9P session
- * @V9FS_EXTENDED: whether or not to use 9P2000.u extensions
+ * @V9FS_PROTO_2000U: whether or not to use 9P2000.u extensions
+ * @V9FS_PROTO_2010L: whether or not to use 9P2010.l extensions
  * @V9FS_ACCESS_SINGLE: only the mounting user can access the hierarchy
  * @V9FS_ACCESS_USER: a new attach will be issued for every user (default)
  * @V9FS_ACCESS_ANY: use a single attach for all users
  * Session flags reflect options selected by users at mount time
  */
 enum p9_session_flags {
-       V9FS_EXTENDED           = 0x01,
-       V9FS_ACCESS_SINGLE      = 0x02,
-       V9FS_ACCESS_USER        = 0x04,
-       V9FS_ACCESS_ANY         = 0x06,
-       V9FS_ACCESS_MASK        = 0x06,
+       V9FS_PROTO_2000U        = 0x01,
+       V9FS_PROTO_2010L        = 0x02,
+       V9FS_ACCESS_SINGLE      = 0x04,
+       V9FS_ACCESS_USER        = 0x08,
+       V9FS_ACCESS_ANY         = 0x0C,
+       V9FS_ACCESS_MASK        = 0x0C,
 };
 
 /* possible values of ->cache */
@@ -121,7 +123,12 @@ static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
        return (inode->i_sb->s_fs_info);
 }
 
-static inline int v9fs_extended(struct v9fs_session_info *v9ses)
+static inline int v9fs_proto_dotu(struct v9fs_session_info *v9ses)
 {
-       return v9ses->flags & V9FS_EXTENDED;
+       return v9ses->flags & V9FS_PROTO_2000U;
+}
+
+static inline int v9fs_proto_dotl(struct v9fs_session_info *v9ses)
+{
+       return v9ses->flags & V9FS_PROTO_2010L;
 }
index 15cce53bf61e7832e92fd8fbc72f08f220a8084a..6580aa4495414bd33a2d260eec8eb95c2bc0a59e 100644 (file)
@@ -135,7 +135,7 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
                while (rdir->head < rdir->tail) {
                        err = p9stat_read(rdir->buf + rdir->head,
                                                buflen - rdir->head, &st,
-                                               fid->clnt->dotu);
+                                               fid->clnt->proto_version);
                        if (err) {
                                P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
                                err = -EIO;
index 74a0461a9ac0dfabd95ca360fa2fa58e32d2e779..36122683fae8df1ff12a21b4454788637f89da1e 100644 (file)
@@ -61,7 +61,7 @@ int v9fs_file_open(struct inode *inode, struct file *file)
 
        P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file);
        v9ses = v9fs_inode2v9ses(inode);
-       omode = v9fs_uflags2omode(file->f_flags, v9fs_extended(v9ses));
+       omode = v9fs_uflags2omode(file->f_flags, v9fs_proto_dotu(v9ses));
        fid = file->private_data;
        if (!fid) {
                fid = v9fs_fid_clone(file->f_path.dentry);
@@ -77,7 +77,7 @@ int v9fs_file_open(struct inode *inode, struct file *file)
                        i_size_write(inode, 0);
                        inode->i_blocks = 0;
                }
-               if ((file->f_flags & O_APPEND) && (!v9fs_extended(v9ses)))
+               if ((file->f_flags & O_APPEND) && (!v9fs_proto_dotu(v9ses)))
                        generic_file_llseek(file, 0, SEEK_END);
        }
 
index a407fa3388c0c560a7ff754066dfa9606380bef5..5fe45d692c9f32d47751209930c3cc4196705671 100644 (file)
@@ -60,7 +60,7 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
        res = mode & 0777;
        if (S_ISDIR(mode))
                res |= P9_DMDIR;
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                if (S_ISLNK(mode))
                        res |= P9_DMSYMLINK;
                if (v9ses->nodev == 0) {
@@ -102,21 +102,21 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
 
        if ((mode & P9_DMDIR) == P9_DMDIR)
                res |= S_IFDIR;
-       else if ((mode & P9_DMSYMLINK) && (v9fs_extended(v9ses)))
+       else if ((mode & P9_DMSYMLINK) && (v9fs_proto_dotu(v9ses)))
                res |= S_IFLNK;
-       else if ((mode & P9_DMSOCKET) && (v9fs_extended(v9ses))
+       else if ((mode & P9_DMSOCKET) && (v9fs_proto_dotu(v9ses))
                 && (v9ses->nodev == 0))
                res |= S_IFSOCK;
-       else if ((mode & P9_DMNAMEDPIPE) && (v9fs_extended(v9ses))
+       else if ((mode & P9_DMNAMEDPIPE) && (v9fs_proto_dotu(v9ses))
                 && (v9ses->nodev == 0))
                res |= S_IFIFO;
-       else if ((mode & P9_DMDEVICE) && (v9fs_extended(v9ses))
+       else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses))
                 && (v9ses->nodev == 0))
                res |= S_IFBLK;
        else
                res |= S_IFREG;
 
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                if ((mode & P9_DMSETUID) == P9_DMSETUID)
                        res |= S_ISUID;
 
@@ -265,7 +265,7 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
        case S_IFBLK:
        case S_IFCHR:
        case S_IFSOCK:
-               if (!v9fs_extended(v9ses)) {
+               if (!v9fs_proto_dotu(v9ses)) {
                        P9_DPRINTK(P9_DEBUG_ERROR,
                                   "special files without extended mode\n");
                        err = -EINVAL;
@@ -278,7 +278,7 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
                inode->i_fop = &v9fs_file_operations;
                break;
        case S_IFLNK:
-               if (!v9fs_extended(v9ses)) {
+               if (!v9fs_proto_dotu(v9ses)) {
                        P9_DPRINTK(P9_DEBUG_ERROR,
                                   "extended modes used w/o 9P2000.u\n");
                        err = -EINVAL;
@@ -288,7 +288,7 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
                break;
        case S_IFDIR:
                inc_nlink(inode);
-               if (v9fs_extended(v9ses))
+               if (v9fs_proto_dotu(v9ses))
                        inode->i_op = &v9fs_dir_inode_operations_ext;
                else
                        inode->i_op = &v9fs_dir_inode_operations;
@@ -575,7 +575,8 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
                flags = O_RDWR;
 
        fid = v9fs_create(v9ses, dir, dentry, NULL, perm,
-                               v9fs_uflags2omode(flags, v9fs_extended(v9ses)));
+                               v9fs_uflags2omode(flags,
+                                               v9fs_proto_dotu(v9ses)));
        if (IS_ERR(fid)) {
                err = PTR_ERR(fid);
                fid = NULL;
@@ -858,7 +859,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
        if (iattr->ia_valid & ATTR_SIZE)
                wstat.length = iattr->ia_size;
 
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                if (iattr->ia_valid & ATTR_UID)
                        wstat.n_uid = iattr->ia_uid;
 
@@ -886,6 +887,8 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
        struct super_block *sb)
 {
        char ext[32];
+       char tag_name[14];
+       unsigned int i_nlink;
        struct v9fs_session_info *v9ses = sb->s_fs_info;
 
        inode->i_nlink = 1;
@@ -897,11 +900,26 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
        inode->i_uid = v9ses->dfltuid;
        inode->i_gid = v9ses->dfltgid;
 
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                inode->i_uid = stat->n_uid;
                inode->i_gid = stat->n_gid;
        }
-
+       if ((S_ISREG(inode->i_mode)) || (S_ISDIR(inode->i_mode))) {
+               if (v9fs_proto_dotu(v9ses) && (stat->extension[0] != '\0')) {
+                       /*
+                        * Hadlink support got added later to
+                        * to the .u extension. So there can be
+                        * server out there that doesn't support
+                        * this even with .u extension. So check
+                        * for non NULL stat->extension
+                        */
+                       strncpy(ext, stat->extension, sizeof(ext));
+                       /* HARDLINKCOUNT %u */
+                       sscanf(ext, "%13s %u", tag_name, &i_nlink);
+                       if (!strncmp(tag_name, "HARDLINKCOUNT", 13))
+                               inode->i_nlink = i_nlink;
+               }
+       }
        inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
        if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
                char type = 0;
@@ -976,7 +994,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
        if (IS_ERR(fid))
                return PTR_ERR(fid);
 
-       if (!v9fs_extended(v9ses))
+       if (!v9fs_proto_dotu(v9ses))
                return -EBADF;
 
        st = p9_client_stat(fid);
@@ -1066,7 +1084,7 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
        struct p9_fid *fid;
 
        v9ses = v9fs_inode2v9ses(dir);
-       if (!v9fs_extended(v9ses)) {
+       if (!v9fs_proto_dotu(v9ses)) {
                P9_DPRINTK(P9_DEBUG_ERROR, "not extended\n");
                return -EPERM;
        }
index 64d44efad7a5d6752ad5c867ff7d10dd0b94b1e3..7405f071be671480ebbc4b79b24f1d11e41bc616 100644 (file)
@@ -177,6 +177,7 @@ source "fs/efs/Kconfig"
 source "fs/jffs2/Kconfig"
 # UBIFS File system configuration
 source "fs/ubifs/Kconfig"
+source "fs/logfs/Kconfig"
 source "fs/cramfs/Kconfig"
 source "fs/squashfs/Kconfig"
 source "fs/freevxfs/Kconfig"
index af6d04700d9c1ff74ff855521d282d6c72fa2bf2..c3633aa46911556ce8dd56e621bfacdc916769a2 100644 (file)
@@ -99,6 +99,7 @@ obj-$(CONFIG_NTFS_FS)         += ntfs/
 obj-$(CONFIG_UFS_FS)           += ufs/
 obj-$(CONFIG_EFS_FS)           += efs/
 obj-$(CONFIG_JFFS2_FS)         += jffs2/
+obj-$(CONFIG_LOGFS)            += logfs/
 obj-$(CONFIG_UBIFS_FS)         += ubifs/
 obj-$(CONFIG_AFFS_FS)          += affs/
 obj-$(CONFIG_ROMFS_FS)         += romfs/
index 9cc18775b832e15eea86ca9b3df7f0304dfde274..2ff622f6f547f5f01d661150e7e41939aa029cc3 100644 (file)
@@ -121,7 +121,7 @@ struct adfs_discmap {
 
 /* Inode stuff */
 struct inode *adfs_iget(struct super_block *sb, struct object_info *obj);
-int adfs_write_inode(struct inode *inode,int unused);
+int adfs_write_inode(struct inode *inode, struct writeback_control *wbc);
 int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
 
 /* map.c */
index 3f57ce4bee5d4371649c2a67354dcc16f4eacb48..0f5e309781355cf8244f67033585451c553c2b68 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
+#include <linux/writeback.h>
 #include "adfs.h"
 
 /*
@@ -360,7 +361,7 @@ out:
  * The adfs-specific inode data has already been updated by
  * adfs_notify_change()
  */
-int adfs_write_inode(struct inode *inode, int wait)
+int adfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct super_block *sb = inode->i_sb;
        struct object_info obj;
@@ -375,7 +376,7 @@ int adfs_write_inode(struct inode *inode, int wait)
        obj.attr        = ADFS_I(inode)->attr;
        obj.size        = inode->i_size;
 
-       ret = adfs_dir_update(sb, &obj, wait);
+       ret = adfs_dir_update(sb, &obj, wbc->sync_mode == WB_SYNC_ALL);
        unlock_kernel();
        return ret;
 }
index 0e40caaba456c064a4960a7f98450a3498bd8cb8..861dae68ac12f0b5ea5861e87997ecb78dc97202 100644 (file)
@@ -175,7 +175,8 @@ extern void                  affs_delete_inode(struct inode *inode);
 extern void                     affs_clear_inode(struct inode *inode);
 extern struct inode            *affs_iget(struct super_block *sb,
                                        unsigned long ino);
-extern int                      affs_write_inode(struct inode *inode, int);
+extern int                      affs_write_inode(struct inode *inode,
+                                       struct writeback_control *wbc);
 extern int                      affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type);
 
 /* file.c */
index 3c4ec7d864c405606f93b8afe0f2963639208e29..c9744d771d98b80d907378b611d90d4576b6132f 100644 (file)
@@ -166,7 +166,7 @@ bad_inode:
 }
 
 int
-affs_write_inode(struct inode *inode, int unused)
+affs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct super_block      *sb = inode->i_sb;
        struct buffer_head      *bh;
index 6ece2a13bf711fd477098d18b60a34a4861fea59..c54dad4e60636ac687608cacc2e5baf70bcb80b0 100644 (file)
@@ -733,7 +733,6 @@ extern int afs_write_end(struct file *file, struct address_space *mapping,
                        struct page *page, void *fsdata);
 extern int afs_writepage(struct page *, struct writeback_control *);
 extern int afs_writepages(struct address_space *, struct writeback_control *);
-extern int afs_write_inode(struct inode *, int);
 extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
 extern ssize_t afs_file_write(struct kiocb *, const struct iovec *,
                              unsigned long, loff_t);
index e1ea1c240b6a2caa7e6b5220bc02380db0e2dea5..14f6431598adfc57d4b563ca98bb40116f87b1ed 100644 (file)
@@ -48,7 +48,6 @@ struct file_system_type afs_fs_type = {
 static const struct super_operations afs_super_ops = {
        .statfs         = afs_statfs,
        .alloc_inode    = afs_alloc_inode,
-       .write_inode    = afs_write_inode,
        .destroy_inode  = afs_destroy_inode,
        .clear_inode    = afs_clear_inode,
        .put_super      = afs_put_super,
index 5e15a21dbf9f3f9d705c2889761e04749c45531a..3bed54a294d4300e234e9c65b49338aced33c7f2 100644 (file)
@@ -584,27 +584,6 @@ int afs_writepages(struct address_space *mapping,
        return ret;
 }
 
-/*
- * write an inode back
- */
-int afs_write_inode(struct inode *inode, int sync)
-{
-       struct afs_vnode *vnode = AFS_FS_I(inode);
-       int ret;
-
-       _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
-
-       ret = 0;
-       if (sync) {
-               ret = filemap_fdatawait(inode->i_mapping);
-               if (ret < 0)
-                       __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
-       }
-
-       _leave(" = %d", ret);
-       return ret;
-}
-
 /*
  * completion of write to server
  */
index 96d394bdaddfa29e52b74d815dd5842e87cb6820..0815e93bb487e0651619d942b6bd78d7fe6a2e89 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -12,7 +12,6 @@
 #include <linux/capability.h>
 #include <linux/fsnotify.h>
 #include <linux/fcntl.h>
-#include <linux/quotaops.h>
 #include <linux/security.h>
 
 /* Taken over from the old code... */
@@ -82,7 +81,7 @@ int inode_newsize_ok(const struct inode *inode, loff_t offset)
        if (inode->i_size < offset) {
                unsigned long limit;
 
-               limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+               limit = rlimit(RLIMIT_FSIZE);
                if (limit != RLIM_INFINITY && offset > limit)
                        goto out_sig;
                if (offset > inode->i_sb->s_maxbytes)
@@ -212,14 +211,8 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
                error = inode->i_op->setattr(dentry, attr);
        } else {
                error = inode_change_ok(inode, attr);
-               if (!error) {
-                       if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
-                           (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
-                               error = vfs_dq_transfer(inode, attr) ?
-                                       -EDQUOT : 0;
-                       if (!error)
-                               error = inode_setattr(inode, attr);
-               }
+               if (!error)
+                       error = inode_setattr(inode, attr);
        }
 
        if (ia_valid & ATTR_SIZE)
index 0118d67221b218672c622fdd1927289c1d9459ed..3d283abf67d7bb0aae5affddee58fdf504706df1 100644 (file)
@@ -60,11 +60,6 @@ do {                                                 \
                current->pid, __func__, ##args);        \
 } while (0)
 
-struct rehash_entry {
-       struct task_struct *task;
-       struct list_head list;
-};
-
 /* Unified info structure.  This is pointed to by both the dentry and
    inode structures.  Each file in the filesystem has an instance of this
    structure.  It holds a reference to the dentry, so dentries are never
@@ -81,7 +76,6 @@ struct autofs_info {
 
        struct list_head active;
        int active_count;
-       struct list_head rehash_list;
 
        struct list_head expiring;
 
@@ -104,7 +98,6 @@ struct autofs_info {
 #define AUTOFS_INF_EXPIRING    (1<<0) /* dentry is in the process of expiring */
 #define AUTOFS_INF_MOUNTPOINT  (1<<1) /* mountpoint status for direct expire */
 #define AUTOFS_INF_PENDING     (1<<2) /* dentry pending mount */
-#define AUTOFS_INF_REHASH      (1<<3) /* dentry in transit to ->lookup() */
 
 struct autofs_wait_queue {
        wait_queue_head_t queue;
index 00bf8fcb245f13cfd7bab5fcd7bc0fdb92952087..c8a80dffb4557d80999b71c66b94cb4a7d2b75fe 100644 (file)
@@ -544,10 +544,9 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
                        goto out;
                devid = new_encode_dev(path.mnt->mnt_sb->s_dev);
                err = 0;
-               if (path.dentry->d_inode &&
-                   path.mnt->mnt_root == path.dentry) {
+               if (path.mnt->mnt_root == path.dentry) {
                        err = 1;
-                       magic = path.dentry->d_inode->i_sb->s_magic;
+                       magic = path.mnt->mnt_sb->s_magic;
                }
        } else {
                dev_t dev = sbi->sb->s_dev;
@@ -560,10 +559,8 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
 
                err = have_submounts(path.dentry);
 
-               if (path.mnt->mnt_mountpoint != path.mnt->mnt_root) {
-                       if (follow_down(&path))
-                               magic = path.mnt->mnt_sb->s_magic;
-               }
+               if (follow_down(&path))
+                       magic = path.mnt->mnt_sb->s_magic;
        }
 
        param->ismountpoint.out.devid = devid;
index 74bc9aa6df310d4ccdf0d312dd43976c02f93d1e..a796c9417fb16f5b4b27ce56586753d3c5b7e948 100644 (file)
@@ -279,7 +279,6 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
                        root->d_mounted--;
                }
                ino->flags |= AUTOFS_INF_EXPIRING;
-               autofs4_add_expiring(root);
                init_completion(&ino->expire_complete);
                spin_unlock(&sbi->fs_lock);
                return root;
@@ -407,7 +406,6 @@ found:
                expired, (int)expired->d_name.len, expired->d_name.name);
        ino = autofs4_dentry_ino(expired);
        ino->flags |= AUTOFS_INF_EXPIRING;
-       autofs4_add_expiring(expired);
        init_completion(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
        spin_lock(&dcache_lock);
@@ -435,7 +433,7 @@ int autofs4_expire_wait(struct dentry *dentry)
 
                DPRINTK("expire done status=%d", status);
 
-               if (d_unhashed(dentry) && IS_DEADDIR(dentry->d_inode))
+               if (d_unhashed(dentry))
                        return -EAGAIN;
 
                return status;
@@ -475,7 +473,6 @@ int autofs4_expire_run(struct super_block *sb,
        spin_lock(&sbi->fs_lock);
        ino = autofs4_dentry_ino(dentry);
        ino->flags &= ~AUTOFS_INF_EXPIRING;
-       autofs4_del_expiring(dentry);
        complete_all(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);
 
@@ -506,7 +503,6 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
                        ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
                }
                ino->flags &= ~AUTOFS_INF_EXPIRING;
-               autofs4_del_expiring(dentry);
                complete_all(&ino->expire_complete);
                spin_unlock(&sbi->fs_lock);
                dput(dentry);
index d0a3de247458aae03f045bf6788b64e5045ad950..821b2b955dac2c7b847a91d7abc91d4e0162c838 100644 (file)
@@ -49,7 +49,6 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
                ino->dentry = NULL;
                ino->size = 0;
                INIT_LIST_HEAD(&ino->active);
-               INIT_LIST_HEAD(&ino->rehash_list);
                ino->active_count = 0;
                INIT_LIST_HEAD(&ino->expiring);
                atomic_set(&ino->count, 0);
@@ -97,63 +96,6 @@ void autofs4_free_ino(struct autofs_info *ino)
        kfree(ino);
 }
 
-/*
- * Deal with the infamous "Busy inodes after umount ..." message.
- *
- * Clean up the dentry tree. This happens with autofs if the user
- * space program goes away due to a SIGKILL, SIGSEGV etc.
- */
-static void autofs4_force_release(struct autofs_sb_info *sbi)
-{
-       struct dentry *this_parent = sbi->sb->s_root;
-       struct list_head *next;
-
-       if (!sbi->sb->s_root)
-               return;
-
-       spin_lock(&dcache_lock);
-repeat:
-       next = this_parent->d_subdirs.next;
-resume:
-       while (next != &this_parent->d_subdirs) {
-               struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
-
-               /* Negative dentry - don`t care */
-               if (!simple_positive(dentry)) {
-                       next = next->next;
-                       continue;
-               }
-
-               if (!list_empty(&dentry->d_subdirs)) {
-                       this_parent = dentry;
-                       goto repeat;
-               }
-
-               next = next->next;
-               spin_unlock(&dcache_lock);
-
-               DPRINTK("dentry %p %.*s",
-                       dentry, (int)dentry->d_name.len, dentry->d_name.name);
-
-               dput(dentry);
-               spin_lock(&dcache_lock);
-       }
-
-       if (this_parent != sbi->sb->s_root) {
-               struct dentry *dentry = this_parent;
-
-               next = this_parent->d_u.d_child.next;
-               this_parent = this_parent->d_parent;
-               spin_unlock(&dcache_lock);
-               DPRINTK("parent dentry %p %.*s",
-                       dentry, (int)dentry->d_name.len, dentry->d_name.name);
-               dput(dentry);
-               spin_lock(&dcache_lock);
-               goto resume;
-       }
-       spin_unlock(&dcache_lock);
-}
-
 void autofs4_kill_sb(struct super_block *sb)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(sb);
@@ -170,15 +112,12 @@ void autofs4_kill_sb(struct super_block *sb)
        /* Free wait queues, close pipe */
        autofs4_catatonic_mode(sbi);
 
-       /* Clean up and release dangling references */
-       autofs4_force_release(sbi);
-
        sb->s_fs_info = NULL;
        kfree(sbi);
 
 out_kill_sb:
        DPRINTK("shutting down");
-       kill_anon_super(sb);
+       kill_litter_super(sb);
 }
 
 static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt)
index 30cc9ddf4b708454c772ca95fc848c5c83ca92df..a015b49891df6347f70f6c318666b63a74638dc8 100644 (file)
@@ -104,99 +104,6 @@ static void autofs4_del_active(struct dentry *dentry)
        return;
 }
 
-static void autofs4_add_rehash_entry(struct autofs_info *ino,
-                                    struct rehash_entry *entry)
-{
-       entry->task = current;
-       INIT_LIST_HEAD(&entry->list);
-       list_add(&entry->list, &ino->rehash_list);
-       return;
-}
-
-static void autofs4_remove_rehash_entry(struct autofs_info *ino)
-{
-       struct list_head *head = &ino->rehash_list;
-       struct rehash_entry *entry;
-       list_for_each_entry(entry, head, list) {
-               if (entry->task == current) {
-                       list_del(&entry->list);
-                       kfree(entry);
-                       break;
-               }
-       }
-       return;
-}
-
-static void autofs4_remove_rehash_entrys(struct autofs_info *ino)
-{
-       struct autofs_sb_info *sbi = ino->sbi;
-       struct rehash_entry *entry, *next;
-       struct list_head *head;
-
-       spin_lock(&sbi->fs_lock);
-       spin_lock(&sbi->lookup_lock);
-       if (!(ino->flags & AUTOFS_INF_REHASH)) {
-               spin_unlock(&sbi->lookup_lock);
-               spin_unlock(&sbi->fs_lock);
-               return;
-       }
-       ino->flags &= ~AUTOFS_INF_REHASH;
-       head = &ino->rehash_list;
-       list_for_each_entry_safe(entry, next, head, list) {
-               list_del(&entry->list);
-               kfree(entry);
-       }
-       spin_unlock(&sbi->lookup_lock);
-       spin_unlock(&sbi->fs_lock);
-       dput(ino->dentry);
-
-       return;
-}
-
-static void autofs4_revalidate_drop(struct dentry *dentry,
-                                   struct rehash_entry *entry)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-       /*
-        * Add to the active list so we can pick this up in
-        * ->lookup(). Also add an entry to a rehash list so
-        * we know when there are no dentrys in flight so we
-        * know when we can rehash the dentry.
-        */
-       spin_lock(&sbi->lookup_lock);
-       if (list_empty(&ino->active))
-               list_add(&ino->active, &sbi->active_list);
-       autofs4_add_rehash_entry(ino, entry);
-       spin_unlock(&sbi->lookup_lock);
-       if (!(ino->flags & AUTOFS_INF_REHASH)) {
-               ino->flags |= AUTOFS_INF_REHASH;
-               dget(dentry);
-               spin_lock(&dentry->d_lock);
-               __d_drop(dentry);
-               spin_unlock(&dentry->d_lock);
-       }
-       return;
-}
-
-static void autofs4_revalidate_rehash(struct dentry *dentry)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-       if (ino->flags & AUTOFS_INF_REHASH) {
-               spin_lock(&sbi->lookup_lock);
-               autofs4_remove_rehash_entry(ino);
-               if (list_empty(&ino->rehash_list)) {
-                       spin_unlock(&sbi->lookup_lock);
-                       ino->flags &= ~AUTOFS_INF_REHASH;
-                       d_rehash(dentry);
-                       dput(ino->dentry);
-               } else
-                       spin_unlock(&sbi->lookup_lock);
-       }
-       return;
-}
-
 static unsigned int autofs4_need_mount(unsigned int flags)
 {
        unsigned int res = 0;
@@ -236,7 +143,7 @@ out:
        return dcache_dir_open(inode, file);
 }
 
-static int try_to_fill_dentry(struct dentry *dentry)
+static int try_to_fill_dentry(struct dentry *dentry, int flags)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
@@ -249,17 +156,55 @@ static int try_to_fill_dentry(struct dentry *dentry)
         * Wait for a pending mount, triggering one if there
         * isn't one already
         */
-       DPRINTK("waiting for mount name=%.*s",
-                dentry->d_name.len, dentry->d_name.name);
+       if (dentry->d_inode == NULL) {
+               DPRINTK("waiting for mount name=%.*s",
+                        dentry->d_name.len, dentry->d_name.name);
 
-       status = autofs4_wait(sbi, dentry, NFY_MOUNT);
+               status = autofs4_wait(sbi, dentry, NFY_MOUNT);
 
-       DPRINTK("mount done status=%d", status);
+               DPRINTK("mount done status=%d", status);
 
-       /* Update expiry counter */
-       ino->last_used = jiffies;
+               /* Turn this into a real negative dentry? */
+               if (status == -ENOENT) {
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags &= ~AUTOFS_INF_PENDING;
+                       spin_unlock(&sbi->fs_lock);
+                       return status;
+               } else if (status) {
+                       /* Return a negative dentry, but leave it "pending" */
+                       return status;
+               }
+       /* Trigger mount for path component or follow link */
+       } else if (ino->flags & AUTOFS_INF_PENDING ||
+                       autofs4_need_mount(flags) ||
+                       current->link_count) {
+               DPRINTK("waiting for mount name=%.*s",
+                       dentry->d_name.len, dentry->d_name.name);
 
-       return status;
+               spin_lock(&sbi->fs_lock);
+               ino->flags |= AUTOFS_INF_PENDING;
+               spin_unlock(&sbi->fs_lock);
+               status = autofs4_wait(sbi, dentry, NFY_MOUNT);
+
+               DPRINTK("mount done status=%d", status);
+
+               if (status) {
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags &= ~AUTOFS_INF_PENDING;
+                       spin_unlock(&sbi->fs_lock);
+                       return status;
+               }
+       }
+
+       /* Initialize expiry counter after successful mount */
+       if (ino)
+               ino->last_used = jiffies;
+
+       spin_lock(&sbi->fs_lock);
+       ino->flags &= ~AUTOFS_INF_PENDING;
+       spin_unlock(&sbi->fs_lock);
+
+       return 0;
 }
 
 /* For autofs direct mounts the follow link triggers the mount */
@@ -313,16 +258,10 @@ static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
         */
        if (ino->flags & AUTOFS_INF_PENDING ||
            (!d_mountpoint(dentry) && list_empty(&dentry->d_subdirs))) {
-               ino->flags |= AUTOFS_INF_PENDING;
                spin_unlock(&dcache_lock);
                spin_unlock(&sbi->fs_lock);
 
-               status = try_to_fill_dentry(dentry);
-
-               spin_lock(&sbi->fs_lock);
-               ino->flags &= ~AUTOFS_INF_PENDING;
-               spin_unlock(&sbi->fs_lock);
-
+               status = try_to_fill_dentry(dentry, 0);
                if (status)
                        goto out_error;
 
@@ -361,47 +300,18 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
        struct inode *dir = dentry->d_parent->d_inode;
        struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-       struct rehash_entry *entry;
+       int oz_mode = autofs4_oz_mode(sbi);
        int flags = nd ? nd->flags : 0;
-       unsigned int mutex_aquired;
+       int status = 1;
 
-       DPRINTK("name = %.*s oz_mode = %d",
-               dentry->d_name.len, dentry->d_name.name, oz_mode);
-
-       /* Daemon never causes a mount to trigger */
-       if (autofs4_oz_mode(sbi))
-               return 1;
-
-       entry = kmalloc(sizeof(struct rehash_entry), GFP_KERNEL);
-       if (!entry)
-               return -ENOMEM;
-
-       mutex_aquired = mutex_trylock(&dir->i_mutex);
-
-       spin_lock(&sbi->fs_lock);
-       spin_lock(&dcache_lock);
        /* Pending dentry */
+       spin_lock(&sbi->fs_lock);
        if (autofs4_ispending(dentry)) {
-               int status;
-
-               /*
-                * We can only unhash and send this to ->lookup() if
-                * the directory mutex is held over d_revalidate() and
-                * ->lookup(). This prevents the VFS from incorrectly
-                * seeing the dentry as non-existent.
-                */
-               ino->flags |= AUTOFS_INF_PENDING;
-               if (!mutex_aquired) {
-                       autofs4_revalidate_drop(dentry, entry);
-                       spin_unlock(&dcache_lock);
-                       spin_unlock(&sbi->fs_lock);
-                       return 0;
-               }
-               spin_unlock(&dcache_lock);
+               /* The daemon never causes a mount to trigger */
                spin_unlock(&sbi->fs_lock);
-               mutex_unlock(&dir->i_mutex);
-               kfree(entry);
+
+               if (oz_mode)
+                       return 1;
 
                /*
                 * If the directory has gone away due to an expire
@@ -415,82 +325,45 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
                 * A zero status is success otherwise we have a
                 * negative error code.
                 */
-               status = try_to_fill_dentry(dentry);
-
-               spin_lock(&sbi->fs_lock);
-               ino->flags &= ~AUTOFS_INF_PENDING;
-               spin_unlock(&sbi->fs_lock);
-
+               status = try_to_fill_dentry(dentry, flags);
                if (status == 0)
                        return 1;
 
                return status;
        }
+       spin_unlock(&sbi->fs_lock);
+
+       /* Negative dentry.. invalidate if "old" */
+       if (dentry->d_inode == NULL)
+               return 0;
 
        /* Check for a non-mountpoint directory with no contents */
+       spin_lock(&dcache_lock);
        if (S_ISDIR(dentry->d_inode->i_mode) &&
            !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) {
                DPRINTK("dentry=%p %.*s, emptydir",
                         dentry, dentry->d_name.len, dentry->d_name.name);
+               spin_unlock(&dcache_lock);
 
-               if (autofs4_need_mount(flags) || current->link_count) {
-                       int status;
-
-                       /*
-                        * We can only unhash and send this to ->lookup() if
-                        * the directory mutex is held over d_revalidate() and
-                        * ->lookup(). This prevents the VFS from incorrectly
-                        * seeing the dentry as non-existent.
-                        */
-                       ino->flags |= AUTOFS_INF_PENDING;
-                       if (!mutex_aquired) {
-                               autofs4_revalidate_drop(dentry, entry);
-                               spin_unlock(&dcache_lock);
-                               spin_unlock(&sbi->fs_lock);
-                               return 0;
-                       }
-                       spin_unlock(&dcache_lock);
-                       spin_unlock(&sbi->fs_lock);
-                       mutex_unlock(&dir->i_mutex);
-                       kfree(entry);
-
-                       /*
-                        * A zero status is success otherwise we have a
-                        * negative error code.
-                        */
-                       status = try_to_fill_dentry(dentry);
-
-                       spin_lock(&sbi->fs_lock);
-                       ino->flags &= ~AUTOFS_INF_PENDING;
-                       spin_unlock(&sbi->fs_lock);
+               /* The daemon never causes a mount to trigger */
+               if (oz_mode)
+                       return 1;
 
-                       if (status == 0)
-                               return 1;
+               /*
+                * A zero status is success otherwise we have a
+                * negative error code.
+                */
+               status = try_to_fill_dentry(dentry, flags);
+               if (status == 0)
+                       return 1;
 
-                       return status;
-               }
+               return status;
        }
        spin_unlock(&dcache_lock);
-       spin_unlock(&sbi->fs_lock);
-
-       if (mutex_aquired)
-               mutex_unlock(&dir->i_mutex);
-
-       kfree(entry);
 
        return 1;
 }
 
-static void autofs4_free_rehash_entrys(struct autofs_info *inf)
-{
-       struct list_head *head = &inf->rehash_list;
-       struct rehash_entry *entry, *next;
-       list_for_each_entry_safe(entry, next, head, list) {
-               list_del(&entry->list);
-               kfree(entry);
-       }
-}
-
 void autofs4_dentry_release(struct dentry *de)
 {
        struct autofs_info *inf;
@@ -509,8 +382,6 @@ void autofs4_dentry_release(struct dentry *de)
                                list_del(&inf->active);
                        if (!list_empty(&inf->expiring))
                                list_del(&inf->expiring);
-                       if (!list_empty(&inf->rehash_list))
-                               autofs4_free_rehash_entrys(inf);
                        spin_unlock(&sbi->lookup_lock);
                }
 
@@ -543,7 +414,6 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry)
        const unsigned char *str = name->name;
        struct list_head *p, *head;
 
-restart:
        spin_lock(&dcache_lock);
        spin_lock(&sbi->lookup_lock);
        head = &sbi->active_list;
@@ -561,19 +431,6 @@ restart:
                if (atomic_read(&active->d_count) == 0)
                        goto next;
 
-               if (active->d_inode && IS_DEADDIR(active->d_inode)) {
-                       if (!list_empty(&ino->rehash_list)) {
-                               dget(active);
-                               spin_unlock(&active->d_lock);
-                               spin_unlock(&sbi->lookup_lock);
-                               spin_unlock(&dcache_lock);
-                               autofs4_remove_rehash_entrys(ino);
-                               dput(active);
-                               goto restart;
-                       }
-                       goto next;
-               }
-
                qstr = &active->d_name;
 
                if (active->d_name.hash != hash)
@@ -586,11 +443,13 @@ restart:
                if (memcmp(qstr->name, str, len))
                        goto next;
 
-               dget(active);
-               spin_unlock(&active->d_lock);
-               spin_unlock(&sbi->lookup_lock);
-               spin_unlock(&dcache_lock);
-               return active;
+               if (d_unhashed(active)) {
+                       dget(active);
+                       spin_unlock(&active->d_lock);
+                       spin_unlock(&sbi->lookup_lock);
+                       spin_unlock(&dcache_lock);
+                       return active;
+               }
 next:
                spin_unlock(&active->d_lock);
        }
@@ -639,11 +498,13 @@ static struct dentry *autofs4_lookup_expiring(struct dentry *dentry)
                if (memcmp(qstr->name, str, len))
                        goto next;
 
-               dget(expiring);
-               spin_unlock(&expiring->d_lock);
-               spin_unlock(&sbi->lookup_lock);
-               spin_unlock(&dcache_lock);
-               return expiring;
+               if (d_unhashed(expiring)) {
+                       dget(expiring);
+                       spin_unlock(&expiring->d_lock);
+                       spin_unlock(&sbi->lookup_lock);
+                       spin_unlock(&dcache_lock);
+                       return expiring;
+               }
 next:
                spin_unlock(&expiring->d_lock);
        }
@@ -653,48 +514,6 @@ next:
        return NULL;
 }
 
-static struct autofs_info *init_new_dentry(struct autofs_sb_info *sbi,
-                                          struct dentry *dentry, int oz_mode)
-{
-       struct autofs_info *ino;
-
-       /*
-        * Mark the dentry incomplete but don't hash it. We do this
-        * to serialize our inode creation operations (symlink and
-        * mkdir) which prevents deadlock during the callback to
-        * the daemon. Subsequent user space lookups for the same
-        * dentry are placed on the wait queue while the daemon
-        * itself is allowed passage unresticted so the create
-        * operation itself can then hash the dentry. Finally,
-        * we check for the hashed dentry and return the newly
-        * hashed dentry.
-        */
-       dentry->d_op = &autofs4_root_dentry_operations;
-
-       /*
-        * And we need to ensure that the same dentry is used for
-        * all following lookup calls until it is hashed so that
-        * the dentry flags are persistent throughout the request.
-        */
-       ino = autofs4_init_ino(NULL, sbi, 0555);
-       if (!ino)
-               return ERR_PTR(-ENOMEM);
-
-       dentry->d_fsdata = ino;
-       ino->dentry = dentry;
-
-       /*
-        * Only set the mount pending flag for new dentrys not created
-        * by the daemon.
-        */
-       if (!oz_mode)
-               ino->flags |= AUTOFS_INF_PENDING;
-
-       d_instantiate(dentry, NULL);
-
-       return ino;
-}
-
 /* Lookups in the root directory */
 static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 {
@@ -702,7 +521,6 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
        struct autofs_info *ino;
        struct dentry *expiring, *active;
        int oz_mode;
-       int status = 0;
 
        DPRINTK("name = %.*s",
                dentry->d_name.len, dentry->d_name.name);
@@ -717,26 +535,44 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
        DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
                 current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
 
-       spin_lock(&sbi->fs_lock);
        active = autofs4_lookup_active(dentry);
        if (active) {
                dentry = active;
                ino = autofs4_dentry_ino(dentry);
-               /* If this came from revalidate, rehash it */
-               autofs4_revalidate_rehash(dentry);
-               spin_unlock(&sbi->fs_lock);
        } else {
-               spin_unlock(&sbi->fs_lock);
-               ino = init_new_dentry(sbi, dentry, oz_mode);
-               if (IS_ERR(ino))
-                       return (struct dentry *) ino;
-       }
+               /*
+                * Mark the dentry incomplete but don't hash it. We do this
+                * to serialize our inode creation operations (symlink and
+                * mkdir) which prevents deadlock during the callback to
+                * the daemon. Subsequent user space lookups for the same
+                * dentry are placed on the wait queue while the daemon
+                * itself is allowed passage unresticted so the create
+                * operation itself can then hash the dentry. Finally,
+                * we check for the hashed dentry and return the newly
+                * hashed dentry.
+                */
+               dentry->d_op = &autofs4_root_dentry_operations;
+
+               /*
+                * And we need to ensure that the same dentry is used for
+                * all following lookup calls until it is hashed so that
+                * the dentry flags are persistent throughout the request.
+                */
+               ino = autofs4_init_ino(NULL, sbi, 0555);
+               if (!ino)
+                       return ERR_PTR(-ENOMEM);
 
-       autofs4_add_active(dentry);
+               dentry->d_fsdata = ino;
+               ino->dentry = dentry;
+
+               autofs4_add_active(dentry);
+
+               d_instantiate(dentry, NULL);
+       }
 
        if (!oz_mode) {
-               expiring = autofs4_lookup_expiring(dentry);
                mutex_unlock(&dir->i_mutex);
+               expiring = autofs4_lookup_expiring(dentry);
                if (expiring) {
                        /*
                         * If we are racing with expire the request might not
@@ -744,22 +580,23 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
                         * so it must have been successful, so just wait for it.
                         */
                        autofs4_expire_wait(expiring);
+                       autofs4_del_expiring(expiring);
                        dput(expiring);
                }
-               status = try_to_fill_dentry(dentry);
-               mutex_lock(&dir->i_mutex);
+
                spin_lock(&sbi->fs_lock);
-               ino->flags &= ~AUTOFS_INF_PENDING;
+               ino->flags |= AUTOFS_INF_PENDING;
                spin_unlock(&sbi->fs_lock);
+               if (dentry->d_op && dentry->d_op->d_revalidate)
+                       (dentry->d_op->d_revalidate)(dentry, nd);
+               mutex_lock(&dir->i_mutex);
        }
 
-       autofs4_del_active(dentry);
-
        /*
-        * If we had a mount fail, check if we had to handle
+        * If we are still pending, check if we had to handle
         * a signal. If so we can force a restart..
         */
-       if (status) {
+       if (ino->flags & AUTOFS_INF_PENDING) {
                /* See if we were interrupted */
                if (signal_pending(current)) {
                        sigset_t *sigset = &current->pending.signal;
@@ -771,46 +608,43 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
                            return ERR_PTR(-ERESTARTNOINTR);
                        }
                }
-       }
-
-       /*
-        * User space can (and has done in the past) remove and re-create
-        * this directory during the callback. This can leave us with an
-        * unhashed dentry, but a successful mount!  So we need to
-        * perform another cached lookup in case the dentry now exists.
-        */
-       if (!oz_mode && !have_submounts(dentry)) {
-               struct dentry *new;
-               new = d_lookup(dentry->d_parent, &dentry->d_name);
-               if (new) {
-                       if (active)
-                               dput(active);
-                       return new;
-               } else {
-                       if (!status)
-                               status = -ENOENT;
+               if (!oz_mode) {
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags &= ~AUTOFS_INF_PENDING;
+                       spin_unlock(&sbi->fs_lock);
                }
        }
 
        /*
-        * If we had a mount failure, return status to user space.
-        * If the mount succeeded and we used a dentry from the active queue
-        * return it.
+        * If this dentry is unhashed, then we shouldn't honour this
+        * lookup.  Returning ENOENT here doesn't do the right thing
+        * for all system calls, but it should be OK for the operations
+        * we permit from an autofs.
         */
-       if (status) {
-               dentry = ERR_PTR(status);
-               if (active)
-                       dput(active);
-               return dentry;
-       } else {
+       if (!oz_mode && d_unhashed(dentry)) {
                /*
-                * Valid successful mount, return active dentry or NULL
-                * for a new dentry.
+                * A user space application can (and has done in the past)
+                * remove and re-create this directory during the callback.
+                * This can leave us with an unhashed dentry, but a
+                * successful mount!  So we need to perform another
+                * cached lookup in case the dentry now exists.
                 */
+               struct dentry *parent = dentry->d_parent;
+               struct dentry *new = d_lookup(parent, &dentry->d_name);
+               if (new != NULL)
+                       dentry = new;
+               else
+                       dentry = ERR_PTR(-ENOENT);
+
                if (active)
-                       return active;
+                       dput(active);
+
+               return dentry;
        }
 
+       if (active)
+               return active;
+
        return NULL;
 }
 
@@ -834,6 +668,8 @@ static int autofs4_dir_symlink(struct inode *dir,
        if (!ino)
                return -ENOMEM;
 
+       autofs4_del_active(dentry);
+
        ino->size = strlen(symname);
        cp = kmalloc(ino->size + 1, GFP_KERNEL);
        if (!cp) {
@@ -910,6 +746,7 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
        dir->i_mtime = CURRENT_TIME;
 
        spin_lock(&dcache_lock);
+       autofs4_add_expiring(dentry);
        spin_lock(&dentry->d_lock);
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
@@ -935,6 +772,7 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
                spin_unlock(&dcache_lock);
                return -ENOTEMPTY;
        }
+       autofs4_add_expiring(dentry);
        spin_lock(&dentry->d_lock);
        __d_drop(dentry);
        spin_unlock(&dentry->d_lock);
@@ -972,6 +810,8 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (!ino)
                return -ENOMEM;
 
+       autofs4_del_active(dentry);
+
        inode = autofs4_get_inode(dir->i_sb, ino);
        if (!inode) {
                if (!dentry->d_fsdata)
index 8f3d9fd896044ffa718b3fc569a613b310c1c3ec..f22a7d3dc362d04cab33cda0ffce245f3dbf8cbd 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/vfs.h>
+#include <linux/writeback.h>
 #include <asm/uaccess.h>
 #include "bfs.h"
 
@@ -98,7 +99,7 @@ error:
        return ERR_PTR(-EIO);
 }
 
-static int bfs_write_inode(struct inode *inode, int wait)
+static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct bfs_sb_info *info = BFS_SB(inode->i_sb);
        unsigned int ino = (u16)inode->i_ino;
@@ -147,7 +148,7 @@ static int bfs_write_inode(struct inode *inode, int wait)
        di->i_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode->i_size - 1);
 
        mark_buffer_dirty(bh);
-       if (wait) {
+       if (wbc->sync_mode == WB_SYNC_ALL) {
                sync_dirty_buffer(bh);
                if (buffer_req(bh) && !buffer_uptodate(bh))
                        err = -EIO;
index fdd3970991722d9ff70306cb6a6635c832c11ea8..15d80bb35d6f7c9e10adcb7ac62d587813449870 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/binfmts.h>
 #include <linux/personality.h>
 #include <linux/init.h>
+#include <linux/coredump.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -59,26 +60,6 @@ static int set_brk(unsigned long start, unsigned long end)
        return 0;
 }
 
-/*
- * These are the only things you should do on a core-file: use only these
- * macros to write out all the necessary info.
- */
-
-static int dump_write(struct file *file, const void *addr, int nr)
-{
-       return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-}
-
-#define DUMP_WRITE(addr, nr)   \
-       if (!dump_write(file, (void *)(addr), (nr))) \
-               goto end_coredump;
-
-#define DUMP_SEEK(offset) \
-if (file->f_op->llseek) { \
-       if (file->f_op->llseek(file,(offset),0) != (offset)) \
-               goto end_coredump; \
-} else file->f_pos = (offset)
-
 /*
  * Routine writes a core dump image in the current directory.
  * Currently only a stub-function.
@@ -130,26 +111,31 @@ static int aout_core_dump(struct coredump_params *cprm)
 
        set_fs(KERNEL_DS);
 /* struct user */
-       DUMP_WRITE(&dump,sizeof(dump));
+       if (!dump_write(file, &dump, sizeof(dump)))
+               goto end_coredump;
 /* Now dump all of the user data.  Include malloced stuff as well */
-       DUMP_SEEK(PAGE_SIZE);
+       if (!dump_seek(cprm->file, PAGE_SIZE - sizeof(dump)))
+               goto end_coredump;
 /* now we start writing out the user space info */
        set_fs(USER_DS);
 /* Dump the data area */
        if (dump.u_dsize != 0) {
                dump_start = START_DATA(dump);
                dump_size = dump.u_dsize << PAGE_SHIFT;
-               DUMP_WRITE(dump_start,dump_size);
+               if (!dump_write(file, dump_start, dump_size))
+                       goto end_coredump;
        }
 /* Now prepare to dump the stack area */
        if (dump.u_ssize != 0) {
                dump_start = START_STACK(dump);
                dump_size = dump.u_ssize << PAGE_SHIFT;
-               DUMP_WRITE(dump_start,dump_size);
+               if (!dump_write(file, dump_start, dump_size))
+                       goto end_coredump;
        }
 /* Finally dump the task struct.  Not be used by gdb, but could be useful */
        set_fs(KERNEL_DS);
-       DUMP_WRITE(current,sizeof(*current));
+       if (!dump_write(file, current, sizeof(*current)))
+               goto end_coredump;
 end_coredump:
        set_fs(fs);
        return has_dumped;
@@ -247,7 +233,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
         * size limits imposed on them by creating programs with large
         * arrays in the data or bss.
         */
-       rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
+       rlim = rlimit(RLIMIT_DATA);
        if (rlim >= RLIM_INFINITY)
                rlim = ~0;
        if (ex.a_data + ex.a_bss > rlim)
index fd5b2ea5d2993bbafa4b370fee3aba3f172577c0..535e763ab1a61e56ef5d25d725dc4f39eb899a49 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/random.h>
 #include <linux/elf.h>
 #include <linux/utsname.h>
+#include <linux/coredump.h>
 #include <asm/uaccess.h>
 #include <asm/param.h>
 #include <asm/page.h>
@@ -1085,36 +1086,6 @@ out:
  * Modelled on fs/exec.c:aout_core_dump()
  * Jeremy Fitzhardinge <jeremy@sw.oz.au>
  */
-/*
- * These are the only things you should do on a core-file: use only these
- * functions to write out all the necessary info.
- */
-static int dump_write(struct file *file, const void *addr, int nr)
-{
-       return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-}
-
-static int dump_seek(struct file *file, loff_t off)
-{
-       if (file->f_op->llseek && file->f_op->llseek != no_llseek) {
-               if (file->f_op->llseek(file, off, SEEK_CUR) < 0)
-                       return 0;
-       } else {
-               char *buf = (char *)get_zeroed_page(GFP_KERNEL);
-               if (!buf)
-                       return 0;
-               while (off > 0) {
-                       unsigned long n = off;
-                       if (n > PAGE_SIZE)
-                               n = PAGE_SIZE;
-                       if (!dump_write(file, buf, n))
-                               return 0;
-                       off -= n;
-               }
-               free_page((unsigned long)buf);
-       }
-       return 1;
-}
 
 /*
  * Decide what to dump of a segment, part, all or none.
@@ -1249,11 +1220,6 @@ static int writenote(struct memelfnote *men, struct file *file,
 }
 #undef DUMP_WRITE
 
-#define DUMP_WRITE(addr, nr)                           \
-       if ((size += (nr)) > cprm->limit ||             \
-           !dump_write(cprm->file, (addr), (nr)))      \
-               goto end_coredump;
-
 static void fill_elf_header(struct elfhdr *elf, int segs,
                            u16 machine, u32 flags, u8 osabi)
 {
@@ -1872,6 +1838,34 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
        return gate_vma;
 }
 
+static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
+                            elf_addr_t e_shoff, int segs)
+{
+       elf->e_shoff = e_shoff;
+       elf->e_shentsize = sizeof(*shdr4extnum);
+       elf->e_shnum = 1;
+       elf->e_shstrndx = SHN_UNDEF;
+
+       memset(shdr4extnum, 0, sizeof(*shdr4extnum));
+
+       shdr4extnum->sh_type = SHT_NULL;
+       shdr4extnum->sh_size = elf->e_shnum;
+       shdr4extnum->sh_link = elf->e_shstrndx;
+       shdr4extnum->sh_info = segs;
+}
+
+static size_t elf_core_vma_data_size(struct vm_area_struct *gate_vma,
+                                    unsigned long mm_flags)
+{
+       struct vm_area_struct *vma;
+       size_t size = 0;
+
+       for (vma = first_vma(current, gate_vma); vma != NULL;
+            vma = next_vma(vma, gate_vma))
+               size += vma_dump_size(vma, mm_flags);
+       return size;
+}
+
 /*
  * Actual dumper
  *
@@ -1888,8 +1882,11 @@ static int elf_core_dump(struct coredump_params *cprm)
        struct vm_area_struct *vma, *gate_vma;
        struct elfhdr *elf = NULL;
        loff_t offset = 0, dataoff, foffset;
-       unsigned long mm_flags;
        struct elf_note_info info;
+       struct elf_phdr *phdr4note = NULL;
+       struct elf_shdr *shdr4extnum = NULL;
+       Elf_Half e_phnum;
+       elf_addr_t e_shoff;
 
        /*
         * We no longer stop all VM operations.
@@ -1912,20 +1909,25 @@ static int elf_core_dump(struct coredump_params *cprm)
         * Please check DEFAULT_MAX_MAP_COUNT definition when you modify here.
         */
        segs = current->mm->map_count;
-#ifdef ELF_CORE_EXTRA_PHDRS
-       segs += ELF_CORE_EXTRA_PHDRS;
-#endif
+       segs += elf_core_extra_phdrs();
 
        gate_vma = get_gate_vma(current);
        if (gate_vma != NULL)
                segs++;
 
+       /* for notes section */
+       segs++;
+
+       /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
+        * this, kernel supports extended numbering. Have a look at
+        * include/linux/elf.h for further information. */
+       e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
+
        /*
         * Collect all the non-memory information about the process for the
         * notes.  This also sets up the file header.
         */
-       if (!fill_note_info(elf, segs + 1, /* including notes section */
-                           &info, cprm->signr, cprm->regs))
+       if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs))
                goto cleanup;
 
        has_dumped = 1;
@@ -1934,31 +1936,47 @@ static int elf_core_dump(struct coredump_params *cprm)
        fs = get_fs();
        set_fs(KERNEL_DS);
 
-       DUMP_WRITE(elf, sizeof(*elf));
        offset += sizeof(*elf);                         /* Elf header */
-       offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */
+       offset += segs * sizeof(struct elf_phdr);       /* Program headers */
        foffset = offset;
 
        /* Write notes phdr entry */
        {
-               struct elf_phdr phdr;
                size_t sz = get_note_info_size(&info);
 
                sz += elf_coredump_extra_notes_size();
 
-               fill_elf_note_phdr(&phdr, sz, offset);
+               phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL);
+               if (!phdr4note)
+                       goto end_coredump;
+
+               fill_elf_note_phdr(phdr4note, sz, offset);
                offset += sz;
-               DUMP_WRITE(&phdr, sizeof(phdr));
        }
 
        dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
 
-       /*
-        * We must use the same mm->flags while dumping core to avoid
-        * inconsistency between the program headers and bodies, otherwise an
-        * unusable core file can be generated.
-        */
-       mm_flags = current->mm->flags;
+       offset += elf_core_vma_data_size(gate_vma, cprm->mm_flags);
+       offset += elf_core_extra_data_size();
+       e_shoff = offset;
+
+       if (e_phnum == PN_XNUM) {
+               shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
+               if (!shdr4extnum)
+                       goto end_coredump;
+               fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
+       }
+
+       offset = dataoff;
+
+       size += sizeof(*elf);
+       if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
+               goto end_coredump;
+
+       size += sizeof(*phdr4note);
+       if (size > cprm->limit
+           || !dump_write(cprm->file, phdr4note, sizeof(*phdr4note)))
+               goto end_coredump;
 
        /* Write program headers for segments dump */
        for (vma = first_vma(current, gate_vma); vma != NULL;
@@ -1969,7 +1987,7 @@ static int elf_core_dump(struct coredump_params *cprm)
                phdr.p_offset = offset;
                phdr.p_vaddr = vma->vm_start;
                phdr.p_paddr = 0;
-               phdr.p_filesz = vma_dump_size(vma, mm_flags);
+               phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags);
                phdr.p_memsz = vma->vm_end - vma->vm_start;
                offset += phdr.p_filesz;
                phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -1979,12 +1997,14 @@ static int elf_core_dump(struct coredump_params *cprm)
                        phdr.p_flags |= PF_X;
                phdr.p_align = ELF_EXEC_PAGESIZE;
 
-               DUMP_WRITE(&phdr, sizeof(phdr));
+               size += sizeof(phdr);
+               if (size > cprm->limit
+                   || !dump_write(cprm->file, &phdr, sizeof(phdr)))
+                       goto end_coredump;
        }
 
-#ifdef ELF_CORE_WRITE_EXTRA_PHDRS
-       ELF_CORE_WRITE_EXTRA_PHDRS;
-#endif
+       if (!elf_core_write_extra_phdrs(cprm->file, offset, &size, cprm->limit))
+               goto end_coredump;
 
        /* write out the notes section */
        if (!write_note_info(&info, cprm->file, &foffset))
@@ -2002,7 +2022,7 @@ static int elf_core_dump(struct coredump_params *cprm)
                unsigned long addr;
                unsigned long end;
 
-               end = vma->vm_start + vma_dump_size(vma, mm_flags);
+               end = vma->vm_start + vma_dump_size(vma, cprm->mm_flags);
 
                for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
                        struct page *page;
@@ -2023,15 +2043,24 @@ static int elf_core_dump(struct coredump_params *cprm)
                }
        }
 
-#ifdef ELF_CORE_WRITE_EXTRA_DATA
-       ELF_CORE_WRITE_EXTRA_DATA;
-#endif
+       if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit))
+               goto end_coredump;
+
+       if (e_phnum == PN_XNUM) {
+               size += sizeof(*shdr4extnum);
+               if (size > cprm->limit
+                   || !dump_write(cprm->file, shdr4extnum,
+                                  sizeof(*shdr4extnum)))
+                       goto end_coredump;
+       }
 
 end_coredump:
        set_fs(fs);
 
 cleanup:
        free_note_info(&info);
+       kfree(shdr4extnum);
+       kfree(phdr4note);
        kfree(elf);
 out:
        return has_dumped;
index 18d77297ccc8ae470a12078d496e7519e443d012..6d6a16c5e9bbb6ba1546543ed42bd93f6305dd76 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/elf.h>
 #include <linux/elf-fdpic.h>
 #include <linux/elfcore.h>
+#include <linux/coredump.h>
 
 #include <asm/uaccess.h>
 #include <asm/param.h>
@@ -1215,26 +1216,6 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
  */
 #ifdef CONFIG_ELF_CORE
 
-/*
- * These are the only things you should do on a core-file: use only these
- * functions to write out all the necessary info.
- */
-static int dump_write(struct file *file, const void *addr, int nr)
-{
-       return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
-}
-
-static int dump_seek(struct file *file, loff_t off)
-{
-       if (file->f_op->llseek) {
-               if (file->f_op->llseek(file, off, SEEK_SET) != off)
-                       return 0;
-       } else {
-               file->f_pos = off;
-       }
-       return 1;
-}
-
 /*
  * Decide whether a segment is worth dumping; default is yes to be
  * sure (missing info is worse than too much; etc).
@@ -1313,35 +1294,35 @@ static int notesize(struct memelfnote *en)
 
 /* #define DEBUG */
 
-#define DUMP_WRITE(addr, nr)   \
-       do { if (!dump_write(file, (addr), (nr))) return 0; } while(0)
-#define DUMP_SEEK(off) \
-       do { if (!dump_seek(file, (off))) return 0; } while(0)
+#define DUMP_WRITE(addr, nr, foffset)  \
+       do { if (!dump_write(file, (addr), (nr))) return 0; *foffset += (nr); } while(0)
 
-static int writenote(struct memelfnote *men, struct file *file)
+static int alignfile(struct file *file, loff_t *foffset)
 {
-       struct elf_note en;
+       static const char buf[4] = { 0, };
+       DUMP_WRITE(buf, roundup(*foffset, 4) - *foffset, foffset);
+       return 1;
+}
 
+static int writenote(struct memelfnote *men, struct file *file,
+                       loff_t *foffset)
+{
+       struct elf_note en;
        en.n_namesz = strlen(men->name) + 1;
        en.n_descsz = men->datasz;
        en.n_type = men->type;
 
-       DUMP_WRITE(&en, sizeof(en));
-       DUMP_WRITE(men->name, en.n_namesz);
-       /* XXX - cast from long long to long to avoid need for libgcc.a */
-       DUMP_SEEK(roundup((unsigned long)file->f_pos, 4));      /* XXX */
-       DUMP_WRITE(men->data, men->datasz);
-       DUMP_SEEK(roundup((unsigned long)file->f_pos, 4));      /* XXX */
+       DUMP_WRITE(&en, sizeof(en), foffset);
+       DUMP_WRITE(men->name, en.n_namesz, foffset);
+       if (!alignfile(file, foffset))
+               return 0;
+       DUMP_WRITE(men->data, men->datasz, foffset);
+       if (!alignfile(file, foffset))
+               return 0;
 
        return 1;
 }
 #undef DUMP_WRITE
-#undef DUMP_SEEK
-
-#define DUMP_WRITE(addr, nr)                           \
-       if ((size += (nr)) > cprm->limit ||             \
-           !dump_write(cprm->file, (addr), (nr)))      \
-               goto end_coredump;
 
 static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
 {
@@ -1524,6 +1505,22 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
        return sz;
 }
 
+static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
+                            elf_addr_t e_shoff, int segs)
+{
+       elf->e_shoff = e_shoff;
+       elf->e_shentsize = sizeof(*shdr4extnum);
+       elf->e_shnum = 1;
+       elf->e_shstrndx = SHN_UNDEF;
+
+       memset(shdr4extnum, 0, sizeof(*shdr4extnum));
+
+       shdr4extnum->sh_type = SHT_NULL;
+       shdr4extnum->sh_size = elf->e_shnum;
+       shdr4extnum->sh_link = elf->e_shstrndx;
+       shdr4extnum->sh_info = segs;
+}
+
 /*
  * dump the segments for an MMU process
  */
@@ -1552,7 +1549,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
                                        err = -EIO;
                                kunmap(page);
                                page_cache_release(page);
-                       } else if (!dump_seek(file, file->f_pos + PAGE_SIZE))
+                       } else if (!dump_seek(file, PAGE_SIZE))
                                err = -EFBIG;
                        if (err)
                                goto out;
@@ -1588,6 +1585,17 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
 }
 #endif
 
+static size_t elf_core_vma_data_size(unsigned long mm_flags)
+{
+       struct vm_area_struct *vma;
+       size_t size = 0;
+
+       for (vma = current->mm->mmap; vma; vma->vm_next)
+               if (maydump(vma, mm_flags))
+                       size += vma->vm_end - vma->vm_start;
+       return size;
+}
+
 /*
  * Actual dumper
  *
@@ -1605,7 +1613,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
        int i;
        struct vm_area_struct *vma;
        struct elfhdr *elf = NULL;
-       loff_t offset = 0, dataoff;
+       loff_t offset = 0, dataoff, foffset;
        int numnote;
        struct memelfnote *notes = NULL;
        struct elf_prstatus *prstatus = NULL;   /* NT_PRSTATUS */
@@ -1618,7 +1626,10 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
 #endif
        int thread_status_size = 0;
        elf_addr_t *auxv;
-       unsigned long mm_flags;
+       struct elf_phdr *phdr4note = NULL;
+       struct elf_shdr *shdr4extnum = NULL;
+       Elf_Half e_phnum;
+       elf_addr_t e_shoff;
 
        /*
         * We no longer stop all VM operations.
@@ -1683,12 +1694,18 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
        elf_core_copy_regs(&prstatus->pr_reg, cprm->regs);
 
        segs = current->mm->map_count;
-#ifdef ELF_CORE_EXTRA_PHDRS
-       segs += ELF_CORE_EXTRA_PHDRS;
-#endif
+       segs += elf_core_extra_phdrs();
+
+       /* for notes section */
+       segs++;
+
+       /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
+        * this, kernel supports extended numbering. Have a look at
+        * include/linux/elf.h for further information. */
+       e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
 
        /* Set up header */
-       fill_elf_fdpic_header(elf, segs + 1);   /* including notes section */
+       fill_elf_fdpic_header(elf, e_phnum);
 
        has_dumped = 1;
        current->flags |= PF_DUMPCORE;
@@ -1727,13 +1744,12 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
        fs = get_fs();
        set_fs(KERNEL_DS);
 
-       DUMP_WRITE(elf, sizeof(*elf));
        offset += sizeof(*elf);                         /* Elf header */
-       offset += (segs+1) * sizeof(struct elf_phdr);   /* Program headers */
+       offset += segs * sizeof(struct elf_phdr);       /* Program headers */
+       foffset = offset;
 
        /* Write notes phdr entry */
        {
-               struct elf_phdr phdr;
                int sz = 0;
 
                for (i = 0; i < numnote; i++)
@@ -1741,20 +1757,38 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
 
                sz += thread_status_size;
 
-               fill_elf_note_phdr(&phdr, sz, offset);
+               phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL);
+               if (!phdr4note)
+                       goto end_coredump;
+
+               fill_elf_note_phdr(phdr4note, sz, offset);
                offset += sz;
-               DUMP_WRITE(&phdr, sizeof(phdr));
        }
 
        /* Page-align dumped data */
        dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
 
-       /*
-        * We must use the same mm->flags while dumping core to avoid
-        * inconsistency between the program headers and bodies, otherwise an
-        * unusable core file can be generated.
-        */
-       mm_flags = current->mm->flags;
+       offset += elf_core_vma_data_size(cprm->mm_flags);
+       offset += elf_core_extra_data_size();
+       e_shoff = offset;
+
+       if (e_phnum == PN_XNUM) {
+               shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
+               if (!shdr4extnum)
+                       goto end_coredump;
+               fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
+       }
+
+       offset = dataoff;
+
+       size += sizeof(*elf);
+       if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
+               goto end_coredump;
+
+       size += sizeof(*phdr4note);
+       if (size > cprm->limit
+           || !dump_write(cprm->file, phdr4note, sizeof(*phdr4note)))
+               goto end_coredump;
 
        /* write program headers for segments dump */
        for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
@@ -1767,7 +1801,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
                phdr.p_offset = offset;
                phdr.p_vaddr = vma->vm_start;
                phdr.p_paddr = 0;
-               phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0;
+               phdr.p_filesz = maydump(vma, cprm->mm_flags) ? sz : 0;
                phdr.p_memsz = sz;
                offset += phdr.p_filesz;
                phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -1777,16 +1811,18 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
                        phdr.p_flags |= PF_X;
                phdr.p_align = ELF_EXEC_PAGESIZE;
 
-               DUMP_WRITE(&phdr, sizeof(phdr));
+               size += sizeof(phdr);
+               if (size > cprm->limit
+                   || !dump_write(cprm->file, &phdr, sizeof(phdr)))
+                       goto end_coredump;
        }
 
-#ifdef ELF_CORE_WRITE_EXTRA_PHDRS
-       ELF_CORE_WRITE_EXTRA_PHDRS;
-#endif
+       if (!elf_core_write_extra_phdrs(cprm->file, offset, &size, cprm->limit))
+               goto end_coredump;
 
        /* write out the notes section */
        for (i = 0; i < numnote; i++)
-               if (!writenote(notes + i, cprm->file))
+               if (!writenote(notes + i, cprm->file, &foffset))
                        goto end_coredump;
 
        /* write out the thread status notes section */
@@ -1795,20 +1831,27 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
                                list_entry(t, struct elf_thread_status, list);
 
                for (i = 0; i < tmp->num_notes; i++)
-                       if (!writenote(&tmp->notes[i], cprm->file))
+                       if (!writenote(&tmp->notes[i], cprm->file, &foffset))
                                goto end_coredump;
        }
 
-       if (!dump_seek(cprm->file, dataoff))
+       if (!dump_seek(cprm->file, dataoff - foffset))
                goto end_coredump;
 
        if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
-                                   mm_flags) < 0)
+                                   cprm->mm_flags) < 0)
                goto end_coredump;
 
-#ifdef ELF_CORE_WRITE_EXTRA_DATA
-       ELF_CORE_WRITE_EXTRA_DATA;
-#endif
+       if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit))
+               goto end_coredump;
+
+       if (e_phnum == PN_XNUM) {
+               size += sizeof(*shdr4extnum);
+               if (size > cprm->limit
+                   || !dump_write(cprm->file, shdr4extnum,
+                                  sizeof(*shdr4extnum)))
+                       goto end_coredump;
+       }
 
        if (cprm->file->f_pos != offset) {
                /* Sanity check */
@@ -1826,7 +1869,7 @@ cleanup:
                list_del(tmp);
                kfree(list_entry(tmp, struct elf_thread_status, list));
        }
-
+       kfree(phdr4note);
        kfree(elf);
        kfree(prstatus);
        kfree(psinfo);
index 42c6b4a54445620856c1e0ab7814bf7f763dba9c..e0e769bdca59918bc0cc2f6c202ccae7bdcd5546 100644 (file)
@@ -501,7 +501,7 @@ static int load_flat_file(struct linux_binprm * bprm,
         * size limits imposed on them by creating programs with large
         * arrays in the data or bss.
         */
-       rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
+       rlim = rlimit(RLIMIT_DATA);
        if (rlim >= RLIM_INFINITY)
                rlim = ~0;
        if (data_len + bss_len > rlim) {
index 2aa8ec6a09812051ba5578092c608c3d0c460535..8b5cfdd4bfc1b7b91e6e94ae94e5ee9f2dbaa875 100644 (file)
@@ -2326,7 +2326,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 int btrfs_readpage(struct file *file, struct page *page);
 void btrfs_delete_inode(struct inode *inode);
 void btrfs_put_inode(struct inode *inode);
-int btrfs_write_inode(struct inode *inode, int wait);
+int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc);
 void btrfs_dirty_inode(struct inode *inode);
 struct inode *btrfs_alloc_inode(struct super_block *sb);
 void btrfs_destroy_inode(struct inode *inode);
index 4deb280f8969b78e373257d394106895d0ea82a6..c41db6d45ab6fb2574ecf7a34fae9e43c18a39d3 100644 (file)
@@ -3968,7 +3968,7 @@ err:
        return ret;
 }
 
-int btrfs_write_inode(struct inode *inode, int wait)
+int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_trans_handle *trans;
@@ -3977,7 +3977,7 @@ int btrfs_write_inode(struct inode *inode, int wait)
        if (root->fs_info->btree_inode == inode)
                return 0;
 
-       if (wait) {
+       if (wbc->sync_mode == WB_SYNC_ALL) {
                trans = btrfs_join_transaction(root, 1);
                btrfs_set_trans_block_group(trans, inode);
                ret = btrfs_commit_transaction(trans, root);
index 057e1dae12ab521a98f356a814fd303b99624776..3d8f8a96f5a350a96e732cc65f45bc843ab6f08e 100644 (file)
@@ -2289,9 +2289,9 @@ cifs_oplock_break(struct slow_work *work)
        if (inode && S_ISREG(inode->i_mode)) {
 #ifdef CONFIG_CIFS_EXPERIMENTAL
                if (cinode->clientCanCacheAll == 0)
-                       break_lease(inode, FMODE_READ);
+                       break_lease(inode, O_RDONLY);
                else if (cinode->clientCanCacheRead == 0)
-                       break_lease(inode, FMODE_WRITE);
+                       break_lease(inode, O_WRONLY);
 #endif
                rc = filemap_fdatawrite(inode->i_mapping);
                if (cinode->clientCanCacheRead == 0) {
index 0adced2f296f7a643640e5eef8538a2db05a381d..112e45a17e99717e1226f3b229c5ffe73c8193f3 100644 (file)
 
 #undef elfhdr
 #undef elf_phdr
+#undef elf_shdr
 #undef elf_note
 #undef elf_addr_t
 #define elfhdr         elf32_hdr
 #define elf_phdr       elf32_phdr
+#define elf_shdr       elf32_shdr
 #define elf_note       elf32_note
 #define elf_addr_t     Elf32_Addr
 
index 0ca9ec4a79c3146355f5f206fbaa4524c256ca08..6d55b61bfa7942252944cabf02d90e9418d2d325 100644 (file)
@@ -545,7 +545,7 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, void __user *argp)
                kcmd = MTIOCPOS;
                karg = &pos;
                break;
-       case MTIOCGET32:
+       default:        /* MTIOCGET32 */
                kcmd = MTIOCGET;
                karg = &get;
                break;
@@ -663,7 +663,7 @@ static int raw_ioctl(unsigned fd, unsigned cmd,
 
         switch (cmd) {
         case RAW_SETBIND:
-        case RAW_GETBIND: {
+       default: {      /* RAW_GETBIND */
                 struct raw_config_request req;
                 mm_segment_t oldfs = get_fs();
 
index 953173a293a93e3d8174e7d5955a70f535a75fb5..f1358e5c3a59b3b5f2551980956f84e09355378e 100644 (file)
@@ -257,6 +257,7 @@ kill_it:
        if (dentry)
                goto repeat;
 }
+EXPORT_SYMBOL(dput);
 
 /**
  * d_invalidate - invalidate a dentry
@@ -314,6 +315,7 @@ int d_invalidate(struct dentry * dentry)
        spin_unlock(&dcache_lock);
        return 0;
 }
+EXPORT_SYMBOL(d_invalidate);
 
 /* This should be called _only_ with dcache_lock held */
 
@@ -328,6 +330,7 @@ struct dentry * dget_locked(struct dentry *dentry)
 {
        return __dget_locked(dentry);
 }
+EXPORT_SYMBOL(dget_locked);
 
 /**
  * d_find_alias - grab a hashed alias of inode
@@ -384,6 +387,7 @@ struct dentry * d_find_alias(struct inode *inode)
        }
        return de;
 }
+EXPORT_SYMBOL(d_find_alias);
 
 /*
  *     Try to kill dentries associated with this inode.
@@ -408,6 +412,7 @@ restart:
        }
        spin_unlock(&dcache_lock);
 }
+EXPORT_SYMBOL(d_prune_aliases);
 
 /*
  * Throw away a dentry - free the inode, dput the parent.  This requires that
@@ -610,6 +615,7 @@ void shrink_dcache_sb(struct super_block * sb)
 {
        __shrink_dcache_sb(sb, NULL, 0);
 }
+EXPORT_SYMBOL(shrink_dcache_sb);
 
 /*
  * destroy a single subtree of dentries for unmount
@@ -792,6 +798,7 @@ positive:
        spin_unlock(&dcache_lock);
        return 1;
 }
+EXPORT_SYMBOL(have_submounts);
 
 /*
  * Search the dentry child list for the specified parent,
@@ -876,6 +883,7 @@ void shrink_dcache_parent(struct dentry * parent)
        while ((found = select_parent(parent)) != 0)
                __shrink_dcache_sb(sb, &found, 0);
 }
+EXPORT_SYMBOL(shrink_dcache_parent);
 
 /*
  * Scan `nr' dentries and return the number which remain.
@@ -968,6 +976,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
 
        return dentry;
 }
+EXPORT_SYMBOL(d_alloc);
 
 struct dentry *d_alloc_name(struct dentry *parent, const char *name)
 {
@@ -1012,6 +1021,7 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
        spin_unlock(&dcache_lock);
        security_d_instantiate(entry, inode);
 }
+EXPORT_SYMBOL(d_instantiate);
 
 /**
  * d_instantiate_unique - instantiate a non-aliased dentry
@@ -1108,6 +1118,7 @@ struct dentry * d_alloc_root(struct inode * root_inode)
        }
        return res;
 }
+EXPORT_SYMBOL(d_alloc_root);
 
 static inline struct hlist_head *d_hash(struct dentry *parent,
                                        unsigned long hash)
@@ -1211,7 +1222,6 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
                        BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED));
                        spin_unlock(&dcache_lock);
                        security_d_instantiate(new, inode);
-                       d_rehash(dentry);
                        d_move(new, dentry);
                        iput(inode);
                } else {
@@ -1225,6 +1235,7 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
                d_add(dentry, inode);
        return new;
 }
+EXPORT_SYMBOL(d_splice_alias);
 
 /**
  * d_add_ci - lookup or allocate new dentry with case-exact name
@@ -1314,6 +1325,7 @@ err_out:
        iput(inode);
        return ERR_PTR(error);
 }
+EXPORT_SYMBOL(d_add_ci);
 
 /**
  * d_lookup - search for a dentry
@@ -1357,6 +1369,7 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
        } while (read_seqretry(&rename_lock, seq));
        return dentry;
 }
+EXPORT_SYMBOL(d_lookup);
 
 struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
 {
@@ -1483,6 +1496,7 @@ int d_validate(struct dentry *dentry, struct dentry *dparent)
 out:
        return 0;
 }
+EXPORT_SYMBOL(d_validate);
 
 /*
  * When a file is deleted, we have two options:
@@ -1528,6 +1542,7 @@ void d_delete(struct dentry * dentry)
 
        fsnotify_nameremove(dentry, isdir);
 }
+EXPORT_SYMBOL(d_delete);
 
 static void __d_rehash(struct dentry * entry, struct hlist_head *list)
 {
@@ -1556,6 +1571,7 @@ void d_rehash(struct dentry * entry)
        spin_unlock(&entry->d_lock);
        spin_unlock(&dcache_lock);
 }
+EXPORT_SYMBOL(d_rehash);
 
 /*
  * When switching names, the actual string doesn't strictly have to
@@ -1702,6 +1718,7 @@ void d_move(struct dentry * dentry, struct dentry * target)
        d_move_locked(dentry, target);
        spin_unlock(&dcache_lock);
 }
+EXPORT_SYMBOL(d_move);
 
 /**
  * d_ancestor - search for an ancestor
@@ -1868,6 +1885,7 @@ shouldnt_be_hashed:
        spin_unlock(&dcache_lock);
        BUG();
 }
+EXPORT_SYMBOL_GPL(d_materialise_unique);
 
 static int prepend(char **buffer, int *buflen, const char *str, int namelen)
 {
@@ -2005,6 +2023,7 @@ char *d_path(const struct path *path, char *buf, int buflen)
        path_put(&root);
        return res;
 }
+EXPORT_SYMBOL(d_path);
 
 /*
  * Helper function for dentry_operations.d_dname() members
@@ -2171,6 +2190,30 @@ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
        return result;
 }
 
+int path_is_under(struct path *path1, struct path *path2)
+{
+       struct vfsmount *mnt = path1->mnt;
+       struct dentry *dentry = path1->dentry;
+       int res;
+       spin_lock(&vfsmount_lock);
+       if (mnt != path2->mnt) {
+               for (;;) {
+                       if (mnt->mnt_parent == mnt) {
+                               spin_unlock(&vfsmount_lock);
+                               return 0;
+                       }
+                       if (mnt->mnt_parent == path2->mnt)
+                               break;
+                       mnt = mnt->mnt_parent;
+               }
+               dentry = mnt->mnt_mountpoint;
+       }
+       res = is_subdir(dentry, path2->dentry);
+       spin_unlock(&vfsmount_lock);
+       return res;
+}
+EXPORT_SYMBOL(path_is_under);
+
 void d_genocide(struct dentry *root)
 {
        struct dentry *this_parent = root;
@@ -2228,6 +2271,7 @@ ino_t find_inode_number(struct dentry *dir, struct qstr *name)
        }
        return ino;
 }
+EXPORT_SYMBOL(find_inode_number);
 
 static __initdata unsigned long dhash_entries;
 static int __init set_dhash_entries(char *str)
@@ -2297,6 +2341,7 @@ static void __init dcache_init(void)
 
 /* SLAB cache for __getname() consumers */
 struct kmem_cache *names_cachep __read_mostly;
+EXPORT_SYMBOL(names_cachep);
 
 EXPORT_SYMBOL(d_genocide);
 
@@ -2326,26 +2371,3 @@ void __init vfs_caches_init(unsigned long mempages)
        bdev_cache_init();
        chrdev_init();
 }
-
-EXPORT_SYMBOL(d_alloc);
-EXPORT_SYMBOL(d_alloc_root);
-EXPORT_SYMBOL(d_delete);
-EXPORT_SYMBOL(d_find_alias);
-EXPORT_SYMBOL(d_instantiate);
-EXPORT_SYMBOL(d_invalidate);
-EXPORT_SYMBOL(d_lookup);
-EXPORT_SYMBOL(d_move);
-EXPORT_SYMBOL_GPL(d_materialise_unique);
-EXPORT_SYMBOL(d_path);
-EXPORT_SYMBOL(d_prune_aliases);
-EXPORT_SYMBOL(d_rehash);
-EXPORT_SYMBOL(d_splice_alias);
-EXPORT_SYMBOL(d_add_ci);
-EXPORT_SYMBOL(d_validate);
-EXPORT_SYMBOL(dget_locked);
-EXPORT_SYMBOL(dput);
-EXPORT_SYMBOL(find_inode_number);
-EXPORT_SYMBOL(have_submounts);
-EXPORT_SYMBOL(names_cachep);
-EXPORT_SYMBOL(shrink_dcache_parent);
-EXPORT_SYMBOL(shrink_dcache_sb);
index 274ac865bae8c776180bc28a19d589f902a090c0..049d6c36da0992eb0e7bbf1938ff5ff2a8d35156 100644 (file)
@@ -496,7 +496,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
        }
        d_move(old_dentry, dentry);
        fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name,
-               old_dentry->d_name.name, S_ISDIR(old_dentry->d_inode->i_mode),
+               S_ISDIR(old_dentry->d_inode->i_mode),
                NULL, old_dentry);
        fsnotify_oldname_free(old_name);
        unlock_rename(new_dir, old_dir);
index cce6bbdbdbb11705afbe6c729007ede3b8714e92..49cdaa19e5b9b800f9c23b8770f28e4cb33c8fbf 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -195,7 +195,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
                 *    to work from.
                 */
                rlim = current->signal->rlim;
-               if (size > rlim[RLIMIT_STACK].rlim_cur / 4) {
+               if (size > ACCESS_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4) {
                        put_page(page);
                        return NULL;
                }
@@ -246,6 +246,7 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
        vma->vm_start = vma->vm_end - PAGE_SIZE;
        vma->vm_flags = VM_STACK_FLAGS;
        vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
        err = insert_vm_struct(mm, vma);
        if (err)
                goto err;
@@ -516,7 +517,8 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
        /*
         * cover the whole range: [new_start, old_end)
         */
-       vma_adjust(vma, new_start, old_end, vma->vm_pgoff, NULL);
+       if (vma_adjust(vma, new_start, old_end, vma->vm_pgoff, NULL))
+               return -ENOMEM;
 
        /*
         * move the page tables downwards, on failure we rely on
@@ -547,15 +549,13 @@ static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
        tlb_finish_mmu(tlb, new_end, old_end);
 
        /*
-        * shrink the vma to just the new range.
+        * Shrink the vma to just the new range.  Always succeeds.
         */
        vma_adjust(vma, new_start, new_end, vma->vm_pgoff, NULL);
 
        return 0;
 }
 
-#define EXTRA_STACK_VM_PAGES   20      /* random */
-
 /*
  * Finalizes the stack vm_area_struct. The flags and permissions are updated,
  * the stack is optionally relocated, and some extra space is added.
@@ -577,7 +577,7 @@ int setup_arg_pages(struct linux_binprm *bprm,
 
 #ifdef CONFIG_STACK_GROWSUP
        /* Limit stack size to 1GB */
-       stack_base = current->signal->rlim[RLIMIT_STACK].rlim_max;
+       stack_base = rlimit_max(RLIMIT_STACK);
        if (stack_base > (1 << 30))
                stack_base = 1 << 30;
 
@@ -630,7 +630,7 @@ int setup_arg_pages(struct linux_binprm *bprm,
                        goto out_unlock;
        }
 
-       stack_expand = EXTRA_STACK_VM_PAGES * PAGE_SIZE;
+       stack_expand = 131072UL; /* randomly 32*4k (or 2*64k) pages */
        stack_size = vma->vm_end - vma->vm_start;
        /*
         * Align this down to a page boundary as expand_stack
@@ -718,6 +718,7 @@ static int exec_mmap(struct mm_struct *mm)
        /* Notify parent that we're no longer interested in the old VM */
        tsk = current;
        old_mm = current->mm;
+       sync_mm_rss(tsk, old_mm);
        mm_release(tsk, old_mm);
 
        if (old_mm) {
@@ -1532,7 +1533,7 @@ static int format_corename(char *corename, long signr)
                        /* core limit size */
                        case 'c':
                                rc = snprintf(out_ptr, out_end - out_ptr,
-                                             "%lu", current->signal->rlim[RLIMIT_CORE].rlim_cur);
+                                             "%lu", rlimit(RLIMIT_CORE));
                                if (rc > out_end - out_ptr)
                                        goto out;
                                out_ptr += rc;
@@ -1560,12 +1561,13 @@ out:
        return ispipe;
 }
 
-static int zap_process(struct task_struct *start)
+static int zap_process(struct task_struct *start, int exit_code)
 {
        struct task_struct *t;
        int nr = 0;
 
        start->signal->flags = SIGNAL_GROUP_EXIT;
+       start->signal->group_exit_code = exit_code;
        start->signal->group_stop_count = 0;
 
        t = start;
@@ -1590,8 +1592,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
        spin_lock_irq(&tsk->sighand->siglock);
        if (!signal_group_exit(tsk->signal)) {
                mm->core_state = core_state;
-               tsk->signal->group_exit_code = exit_code;
-               nr = zap_process(tsk);
+               nr = zap_process(tsk, exit_code);
        }
        spin_unlock_irq(&tsk->sighand->siglock);
        if (unlikely(nr < 0))
@@ -1640,7 +1641,7 @@ static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm,
                        if (p->mm) {
                                if (unlikely(p->mm == mm)) {
                                        lock_task_sighand(p, &flags);
-                                       nr += zap_process(p);
+                                       nr += zap_process(p, exit_code);
                                        unlock_task_sighand(p, &flags);
                                }
                                break;
@@ -1747,14 +1748,19 @@ void set_dumpable(struct mm_struct *mm, int value)
        }
 }
 
-int get_dumpable(struct mm_struct *mm)
+static int __get_dumpable(unsigned long mm_flags)
 {
        int ret;
 
-       ret = mm->flags & 0x3;
+       ret = mm_flags & MMF_DUMPABLE_MASK;
        return (ret >= 2) ? 2 : ret;
 }
 
+int get_dumpable(struct mm_struct *mm)
+{
+       return __get_dumpable(mm->flags);
+}
+
 static void wait_for_dump_helpers(struct file *file)
 {
        struct pipe_inode_info *pipe;
@@ -1797,7 +1803,13 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        struct coredump_params cprm = {
                .signr = signr,
                .regs = regs,
-               .limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
+               .limit = rlimit(RLIMIT_CORE),
+               /*
+                * We must use the same mm->flags while dumping core to avoid
+                * inconsistency of bit flags, since this flag is not protected
+                * by any locks.
+                */
+               .mm_flags = mm->flags,
        };
 
        audit_core_dumps(signr);
@@ -1816,7 +1828,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        /*
         * If another thread got here first, or we are not dumpable, bail out.
         */
-       if (mm->core_state || !get_dumpable(mm)) {
+       if (mm->core_state || !__get_dumpable(cprm.mm_flags)) {
                up_write(&mm->mmap_sem);
                put_cred(cred);
                goto fail;
@@ -1827,7 +1839,8 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
         *      process nor do we know its entire history. We only know it
         *      was tainted so we dump it as root in mode 2.
         */
-       if (get_dumpable(mm) == 2) {    /* Setuid core dump mode */
+       if (__get_dumpable(cprm.mm_flags) == 2) {
+               /* Setuid core dump mode */
                flag = O_EXCL;          /* Stop rewrite attacks */
                cred->fsuid = 0;        /* Dump root private */
        }
@@ -1923,8 +1936,9 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        /*
         * Dont allow local users get cute and trick others to coredump
         * into their pre-created files:
+        * Note, this is not relevant for pipes
         */
-       if (inode->i_uid != current_fsuid())
+       if (!ispipe && (inode->i_uid != current_fsuid()))
                goto close_fail;
        if (!cprm.file->f_op)
                goto close_fail;
index b1b178e61718a5a7cee88ee7e2083b9fa5f3a3bd..f0d520312d8b6ed1c3b35dc00675f1a84c0ed263 100644 (file)
@@ -55,6 +55,8 @@
 /* exofs Application specific page/attribute */
 # define EXOFS_APAGE_FS_DATA   (OSD_APAGE_APP_DEFINED_FIRST + 3)
 # define EXOFS_ATTR_INODE_DATA 1
+# define EXOFS_ATTR_INODE_FILE_LAYOUT  2
+# define EXOFS_ATTR_INODE_DIR_LAYOUT   3
 
 /*
  * The maximum number of files we can have is limited by the size of the
@@ -206,4 +208,41 @@ enum {
        (((name_len) + offsetof(struct exofs_dir_entry, name)  + \
          EXOFS_DIR_ROUND) & ~EXOFS_DIR_ROUND)
 
+/*
+ * The on-disk (optional) layout structure.
+ * sits in an EXOFS_ATTR_INODE_FILE_LAYOUT or EXOFS_ATTR_INODE_DIR_LAYOUT
+ * attribute, attached to any inode, usually to a directory.
+ */
+
+enum exofs_inode_layout_gen_functions {
+       LAYOUT_MOVING_WINDOW = 0,
+       LAYOUT_IMPLICT = 1,
+};
+
+struct exofs_on_disk_inode_layout {
+       __le16 gen_func; /* One of enum exofs_inode_layout_gen_functions */
+       __le16 pad;
+       union {
+               /* gen_func == LAYOUT_MOVING_WINDOW (default) */
+               struct exofs_layout_sliding_window {
+                       __le32 num_devices; /* first n devices in global-table*/
+               } sliding_window __packed;
+
+               /* gen_func == LAYOUT_IMPLICT */
+               struct exofs_layout_implict_list {
+                       struct exofs_dt_data_map data_map;
+                       /* Variable array of size data_map.cb_num_comps. These
+                        * are device indexes of the devices in the global table
+                        */
+                       __le32 dev_indexes[];
+               } implict __packed;
+       };
+} __packed;
+
+static inline size_t exofs_on_disk_inode_layout_size(unsigned max_devs)
+{
+       return sizeof(struct exofs_on_disk_inode_layout) +
+               max_devs * sizeof(__le32);
+}
+
 #endif /*ifndef __EXOFS_COM_H__*/
index c35fd4623986cef5479d5edd3039595b788352ee..8442e353309f5d9eb09e39c21f3f7ddc31cc8c3f 100644 (file)
 /* u64 has problems with printk this will cast it to unsigned long long */
 #define _LLU(x) (unsigned long long)(x)
 
+struct exofs_layout {
+       osd_id          s_pid;                  /* partition ID of file system*/
+
+       /* Our way of looking at the data_map */
+       unsigned stripe_unit;
+       unsigned mirrors_p1;
+
+       unsigned group_width;
+       u64      group_depth;
+       unsigned group_count;
+
+       enum exofs_inode_layout_gen_functions lay_func;
+
+       unsigned        s_numdevs;              /* Num of devices in array    */
+       struct osd_dev  *s_ods[0];              /* Variable length            */
+};
+
 /*
  * our extension to the in-memory superblock
  */
 struct exofs_sb_info {
        struct exofs_fscb s_fscb;               /* Written often, pre-allocate*/
-       osd_id          s_pid;                  /* partition ID of file system*/
        int             s_timeout;              /* timeout for OSD operations */
        uint64_t        s_nextid;               /* highest object ID used     */
        uint32_t        s_numfiles;             /* number of files on fs      */
@@ -69,22 +85,27 @@ struct exofs_sb_info {
        atomic_t        s_curr_pending;         /* number of pending commands */
        uint8_t         s_cred[OSD_CAP_LEN];    /* credential for the fscb    */
 
-       struct pnfs_osd_data_map data_map;      /* Default raid to use        */
-       unsigned        s_numdevs;              /* Num of devices in array    */
-       struct osd_dev  *s_ods[1];              /* Variable length, minimum 1 */
+       struct pnfs_osd_data_map data_map;      /* Default raid to use
+                                                * FIXME: Needed ?
+                                                */
+/*     struct exofs_layout     dir_layout;*/   /* Default dir layout */
+       struct exofs_layout     layout;         /* Default files layout,
+                                                * contains the variable osd_dev
+                                                * array. Keep last */
+       struct osd_dev  *_min_one_dev[1];       /* Place holder for one dev   */
 };
 
 /*
  * our extension to the in-memory inode
  */
 struct exofs_i_info {
+       struct inode   vfs_inode;          /* normal in-memory inode          */
+       wait_queue_head_t i_wq;            /* wait queue for inode            */
        unsigned long  i_flags;            /* various atomic flags            */
        uint32_t       i_data[EXOFS_IDATA];/*short symlink names and device #s*/
        uint32_t       i_dir_start_lookup; /* which page to start lookup      */
-       wait_queue_head_t i_wq;            /* wait queue for inode            */
        uint64_t       i_commit_size;      /* the object's written length     */
        uint8_t        i_cred[OSD_CAP_LEN];/* all-powerful credential         */
-       struct inode   vfs_inode;          /* normal in-memory inode          */
 };
 
 static inline osd_id exofs_oi_objno(struct exofs_i_info *oi)
@@ -101,7 +122,7 @@ struct exofs_io_state {
        void                    *private;
        exofs_io_done_fn        done;
 
-       struct exofs_sb_info    *sbi;
+       struct exofs_layout     *layout;
        struct osd_obj_id       obj;
        u8                      *cred;
 
@@ -109,7 +130,11 @@ struct exofs_io_state {
        loff_t                  offset;
        unsigned long           length;
        void                    *kern_buff;
-       struct bio              *bio;
+
+       struct page             **pages;
+       unsigned                nr_pages;
+       unsigned                pgbase;
+       unsigned                pages_consumed;
 
        /* Attributes */
        unsigned                in_attr_len;
@@ -122,6 +147,9 @@ struct exofs_io_state {
        struct exofs_per_dev_state {
                struct osd_request *or;
                struct bio *bio;
+               loff_t offset;
+               unsigned length;
+               unsigned dev;
        } per_dev[];
 };
 
@@ -174,6 +202,12 @@ static inline struct exofs_i_info *exofs_i(struct inode *inode)
        return container_of(inode, struct exofs_i_info, vfs_inode);
 }
 
+/*
+ * Given a layout, object_number and stripe_index return the associated global
+ * dev_index
+ */
+unsigned exofs_layout_od_id(struct exofs_layout *layout,
+                           osd_id obj_no, unsigned layout_index);
 /*
  * Maximum count of links to a file
  */
@@ -189,7 +223,8 @@ void exofs_make_credential(u8 cred_a[OSD_CAP_LEN],
 int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj,
                    u64 offset, void *p, unsigned length);
 
-int  exofs_get_io_state(struct exofs_sb_info *sbi, struct exofs_io_state** ios);
+int  exofs_get_io_state(struct exofs_layout *layout,
+                       struct exofs_io_state **ios);
 void exofs_put_io_state(struct exofs_io_state *ios);
 
 int exofs_check_io(struct exofs_io_state *ios, u64 *resid);
@@ -226,7 +261,7 @@ int exofs_write_begin(struct file *file, struct address_space *mapping,
                struct page **pagep, void **fsdata);
 extern struct inode *exofs_iget(struct super_block *, unsigned long);
 struct inode *exofs_new_inode(struct inode *, int);
-extern int exofs_write_inode(struct inode *, int);
+extern int exofs_write_inode(struct inode *, struct writeback_control *wbc);
 extern void exofs_delete_inode(struct inode *);
 
 /* dir.c:                */
index 2afbcebeda718efa2e468d0ff03ee96cd5e10267..a17e4b733e35116f2c6bb83960a03680e714efe8 100644 (file)
 
 enum { BIO_MAX_PAGES_KMALLOC =
                (PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),
+       MAX_PAGES_KMALLOC =
+               PAGE_SIZE / sizeof(struct page *),
 };
 
 struct page_collect {
        struct exofs_sb_info *sbi;
-       struct request_queue *req_q;
        struct inode *inode;
        unsigned expected_pages;
        struct exofs_io_state *ios;
 
-       struct bio *bio;
+       struct page **pages;
+       unsigned alloc_pages;
        unsigned nr_pages;
        unsigned long length;
        loff_t pg_first; /* keep 64bit also in 32-arches */
@@ -62,15 +64,12 @@ static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
        struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
 
        pcol->sbi = sbi;
-       /* Create master bios on first Q, later on cloning, each clone will be
-        * allocated on it's destination Q
-        */
-       pcol->req_q = osd_request_queue(sbi->s_ods[0]);
        pcol->inode = inode;
        pcol->expected_pages = expected_pages;
 
        pcol->ios = NULL;
-       pcol->bio = NULL;
+       pcol->pages = NULL;
+       pcol->alloc_pages = 0;
        pcol->nr_pages = 0;
        pcol->length = 0;
        pcol->pg_first = -1;
@@ -80,7 +79,8 @@ static void _pcol_reset(struct page_collect *pcol)
 {
        pcol->expected_pages -= min(pcol->nr_pages, pcol->expected_pages);
 
-       pcol->bio = NULL;
+       pcol->pages = NULL;
+       pcol->alloc_pages = 0;
        pcol->nr_pages = 0;
        pcol->length = 0;
        pcol->pg_first = -1;
@@ -90,38 +90,43 @@ static void _pcol_reset(struct page_collect *pcol)
         * it might not end here. don't be left with nothing
         */
        if (!pcol->expected_pages)
-               pcol->expected_pages = BIO_MAX_PAGES_KMALLOC;
+               pcol->expected_pages = MAX_PAGES_KMALLOC;
 }
 
 static int pcol_try_alloc(struct page_collect *pcol)
 {
-       int pages = min_t(unsigned, pcol->expected_pages,
-                         BIO_MAX_PAGES_KMALLOC);
+       unsigned pages = min_t(unsigned, pcol->expected_pages,
+                         MAX_PAGES_KMALLOC);
 
        if (!pcol->ios) { /* First time allocate io_state */
-               int ret = exofs_get_io_state(pcol->sbi, &pcol->ios);
+               int ret = exofs_get_io_state(&pcol->sbi->layout, &pcol->ios);
 
                if (ret)
                        return ret;
        }
 
+       /* TODO: easily support bio chaining */
+       pages =  min_t(unsigned, pages,
+                      pcol->sbi->layout.group_width * BIO_MAX_PAGES_KMALLOC);
+
        for (; pages; pages >>= 1) {
-               pcol->bio = bio_kmalloc(GFP_KERNEL, pages);
-               if (likely(pcol->bio))
+               pcol->pages = kmalloc(pages * sizeof(struct page *),
+                                     GFP_KERNEL);
+               if (likely(pcol->pages)) {
+                       pcol->alloc_pages = pages;
                        return 0;
+               }
        }
 
-       EXOFS_ERR("Failed to bio_kmalloc expected_pages=%u\n",
+       EXOFS_ERR("Failed to kmalloc expected_pages=%u\n",
                  pcol->expected_pages);
        return -ENOMEM;
 }
 
 static void pcol_free(struct page_collect *pcol)
 {
-       if (pcol->bio) {
-               bio_put(pcol->bio);
-               pcol->bio = NULL;
-       }
+       kfree(pcol->pages);
+       pcol->pages = NULL;
 
        if (pcol->ios) {
                exofs_put_io_state(pcol->ios);
@@ -132,11 +137,10 @@ static void pcol_free(struct page_collect *pcol)
 static int pcol_add_page(struct page_collect *pcol, struct page *page,
                         unsigned len)
 {
-       int added_len = bio_add_pc_page(pcol->req_q, pcol->bio, page, len, 0);
-       if (unlikely(len != added_len))
+       if (unlikely(pcol->nr_pages >= pcol->alloc_pages))
                return -ENOMEM;
 
-       ++pcol->nr_pages;
+       pcol->pages[pcol->nr_pages++] = page;
        pcol->length += len;
        return 0;
 }
@@ -181,7 +185,6 @@ static void update_write_page(struct page *page, int ret)
  */
 static int __readpages_done(struct page_collect *pcol, bool do_unlock)
 {
-       struct bio_vec *bvec;
        int i;
        u64 resid;
        u64 good_bytes;
@@ -193,13 +196,13 @@ static int __readpages_done(struct page_collect *pcol, bool do_unlock)
        else
                good_bytes = pcol->length - resid;
 
-       EXOFS_DBGMSG("readpages_done(0x%lx) good_bytes=0x%llx"
+       EXOFS_DBGMSG2("readpages_done(0x%lx) good_bytes=0x%llx"
                     " length=0x%lx nr_pages=%u\n",
                     pcol->inode->i_ino, _LLU(good_bytes), pcol->length,
                     pcol->nr_pages);
 
-       __bio_for_each_segment(bvec, pcol->bio, i, 0) {
-               struct page *page = bvec->bv_page;
+       for (i = 0; i < pcol->nr_pages; i++) {
+               struct page *page = pcol->pages[i];
                struct inode *inode = page->mapping->host;
                int page_stat;
 
@@ -218,11 +221,11 @@ static int __readpages_done(struct page_collect *pcol, bool do_unlock)
                ret = update_read_page(page, page_stat);
                if (do_unlock)
                        unlock_page(page);
-               length += bvec->bv_len;
+               length += PAGE_SIZE;
        }
 
        pcol_free(pcol);
-       EXOFS_DBGMSG("readpages_done END\n");
+       EXOFS_DBGMSG2("readpages_done END\n");
        return ret;
 }
 
@@ -238,11 +241,10 @@ static void readpages_done(struct exofs_io_state *ios, void *p)
 
 static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw)
 {
-       struct bio_vec *bvec;
        int i;
 
-       __bio_for_each_segment(bvec, pcol->bio, i, 0) {
-               struct page *page = bvec->bv_page;
+       for (i = 0; i < pcol->nr_pages; i++) {
+               struct page *page = pcol->pages[i];
 
                if (rw == READ)
                        update_read_page(page, ret);
@@ -260,13 +262,14 @@ static int read_exec(struct page_collect *pcol, bool is_sync)
        struct page_collect *pcol_copy = NULL;
        int ret;
 
-       if (!pcol->bio)
+       if (!pcol->pages)
                return 0;
 
        /* see comment in _readpage() about sync reads */
        WARN_ON(is_sync && (pcol->nr_pages != 1));
 
-       ios->bio = pcol->bio;
+       ios->pages = pcol->pages;
+       ios->nr_pages = pcol->nr_pages;
        ios->length = pcol->length;
        ios->offset = pcol->pg_first << PAGE_CACHE_SHIFT;
 
@@ -290,7 +293,7 @@ static int read_exec(struct page_collect *pcol, bool is_sync)
 
        atomic_inc(&pcol->sbi->s_curr_pending);
 
-       EXOFS_DBGMSG("read_exec obj=0x%llx start=0x%llx length=0x%lx\n",
+       EXOFS_DBGMSG2("read_exec obj=0x%llx start=0x%llx length=0x%lx\n",
                  ios->obj.id, _LLU(ios->offset), pcol->length);
 
        /* pages ownership was passed to pcol_copy */
@@ -366,7 +369,7 @@ try_again:
                goto try_again;
        }
 
-       if (!pcol->bio) {
+       if (!pcol->pages) {
                ret = pcol_try_alloc(pcol);
                if (unlikely(ret))
                        goto fail;
@@ -448,7 +451,6 @@ static int exofs_readpage(struct file *file, struct page *page)
 static void writepages_done(struct exofs_io_state *ios, void *p)
 {
        struct page_collect *pcol = p;
-       struct bio_vec *bvec;
        int i;
        u64 resid;
        u64  good_bytes;
@@ -462,13 +464,13 @@ static void writepages_done(struct exofs_io_state *ios, void *p)
        else
                good_bytes = pcol->length - resid;
 
-       EXOFS_DBGMSG("writepages_done(0x%lx) good_bytes=0x%llx"
+       EXOFS_DBGMSG2("writepages_done(0x%lx) good_bytes=0x%llx"
                     " length=0x%lx nr_pages=%u\n",
                     pcol->inode->i_ino, _LLU(good_bytes), pcol->length,
                     pcol->nr_pages);
 
-       __bio_for_each_segment(bvec, pcol->bio, i, 0) {
-               struct page *page = bvec->bv_page;
+       for (i = 0; i < pcol->nr_pages; i++) {
+               struct page *page = pcol->pages[i];
                struct inode *inode = page->mapping->host;
                int page_stat;
 
@@ -485,12 +487,12 @@ static void writepages_done(struct exofs_io_state *ios, void *p)
                EXOFS_DBGMSG2("    writepages_done(0x%lx, 0x%lx) status=%d\n",
                             inode->i_ino, page->index, page_stat);
 
-               length += bvec->bv_len;
+               length += PAGE_SIZE;
        }
 
        pcol_free(pcol);
        kfree(pcol);
-       EXOFS_DBGMSG("writepages_done END\n");
+       EXOFS_DBGMSG2("writepages_done END\n");
 }
 
 static int write_exec(struct page_collect *pcol)
@@ -500,7 +502,7 @@ static int write_exec(struct page_collect *pcol)
        struct page_collect *pcol_copy = NULL;
        int ret;
 
-       if (!pcol->bio)
+       if (!pcol->pages)
                return 0;
 
        pcol_copy = kmalloc(sizeof(*pcol_copy), GFP_KERNEL);
@@ -512,9 +514,8 @@ static int write_exec(struct page_collect *pcol)
 
        *pcol_copy = *pcol;
 
-       pcol_copy->bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */
-
-       ios->bio = pcol_copy->bio;
+       ios->pages = pcol_copy->pages;
+       ios->nr_pages = pcol_copy->nr_pages;
        ios->offset = pcol_copy->pg_first << PAGE_CACHE_SHIFT;
        ios->length = pcol_copy->length;
        ios->done = writepages_done;
@@ -527,7 +528,7 @@ static int write_exec(struct page_collect *pcol)
        }
 
        atomic_inc(&pcol->sbi->s_curr_pending);
-       EXOFS_DBGMSG("write_exec(0x%lx, 0x%llx) start=0x%llx length=0x%lx\n",
+       EXOFS_DBGMSG2("write_exec(0x%lx, 0x%llx) start=0x%llx length=0x%lx\n",
                  pcol->inode->i_ino, pcol->pg_first, _LLU(ios->offset),
                  pcol->length);
        /* pages ownership was passed to pcol_copy */
@@ -605,7 +606,7 @@ try_again:
                goto try_again;
        }
 
-       if (!pcol->bio) {
+       if (!pcol->pages) {
                ret = pcol_try_alloc(pcol);
                if (unlikely(ret))
                        goto fail;
@@ -616,7 +617,7 @@ try_again:
 
        ret = pcol_add_page(pcol, page, len);
        if (unlikely(ret)) {
-               EXOFS_DBGMSG("Failed pcol_add_page "
+               EXOFS_DBGMSG2("Failed pcol_add_page "
                             "nr_pages=%u total_length=0x%lx\n",
                             pcol->nr_pages, pcol->length);
 
@@ -663,7 +664,7 @@ static int exofs_writepages(struct address_space *mapping,
        if (expected_pages < 32L)
                expected_pages = 32L;
 
-       EXOFS_DBGMSG("inode(0x%lx) wbc->start=0x%llx wbc->end=0x%llx "
+       EXOFS_DBGMSG2("inode(0x%lx) wbc->start=0x%llx wbc->end=0x%llx "
                     "nrpages=%lu start=0x%lx end=0x%lx expected_pages=%ld\n",
                     mapping->host->i_ino, wbc->range_start, wbc->range_end,
                     mapping->nrpages, start, end, expected_pages);
@@ -859,20 +860,33 @@ int exofs_setattr(struct dentry *dentry, struct iattr *iattr)
        return error;
 }
 
+static const struct osd_attr g_attr_inode_file_layout = ATTR_DEF(
+       EXOFS_APAGE_FS_DATA,
+       EXOFS_ATTR_INODE_FILE_LAYOUT,
+       0);
+static const struct osd_attr g_attr_inode_dir_layout = ATTR_DEF(
+       EXOFS_APAGE_FS_DATA,
+       EXOFS_ATTR_INODE_DIR_LAYOUT,
+       0);
+
 /*
- * Read an inode from the OSD, and return it as is.  We also return the size
- * attribute in the 'obj_size' argument.
+ * Read the Linux inode info from the OSD, and return it as is. In exofs the
+ * inode info is in an application specific page/attribute of the osd-object.
  */
 static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi,
-                   struct exofs_fcb *inode, uint64_t *obj_size)
+                   struct exofs_fcb *inode)
 {
        struct exofs_sb_info *sbi = sb->s_fs_info;
-       struct osd_attr attrs[2];
+       struct osd_attr attrs[] = {
+               [0] = g_attr_inode_data,
+               [1] = g_attr_inode_file_layout,
+               [2] = g_attr_inode_dir_layout,
+       };
        struct exofs_io_state *ios;
+       struct exofs_on_disk_inode_layout *layout;
        int ret;
 
-       *obj_size = ~0;
-       ret = exofs_get_io_state(sbi, &ios);
+       ret = exofs_get_io_state(&sbi->layout, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
                return ret;
@@ -882,14 +896,25 @@ static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi,
        exofs_make_credential(oi->i_cred, &ios->obj);
        ios->cred = oi->i_cred;
 
-       attrs[0] = g_attr_inode_data;
-       attrs[1] = g_attr_logical_length;
+       attrs[1].len = exofs_on_disk_inode_layout_size(sbi->layout.s_numdevs);
+       attrs[2].len = exofs_on_disk_inode_layout_size(sbi->layout.s_numdevs);
+
        ios->in_attr = attrs;
        ios->in_attr_len = ARRAY_SIZE(attrs);
 
        ret = exofs_sbi_read(ios);
-       if (ret)
+       if (unlikely(ret)) {
+               EXOFS_ERR("object(0x%llx) corrupted, return empty file=>%d\n",
+                         _LLU(ios->obj.id), ret);
+               memset(inode, 0, sizeof(*inode));
+               inode->i_mode = 0040000 | (0777 & ~022);
+               /* If object is lost on target we might as well enable it's
+                * delete.
+                */
+               if ((ret == -ENOENT) || (ret == -EINVAL))
+                       ret = 0;
                goto out;
+       }
 
        ret = extract_attr_from_ios(ios, &attrs[0]);
        if (ret) {
@@ -901,11 +926,33 @@ static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi,
 
        ret = extract_attr_from_ios(ios, &attrs[1]);
        if (ret) {
-               EXOFS_ERR("%s: extract_attr of logical_length failed\n",
-                         __func__);
+               EXOFS_ERR("%s: extract_attr of inode_data failed\n", __func__);
+               goto out;
+       }
+       if (attrs[1].len) {
+               layout = attrs[1].val_ptr;
+               if (layout->gen_func != cpu_to_le16(LAYOUT_MOVING_WINDOW)) {
+                       EXOFS_ERR("%s: unsupported files layout %d\n",
+                               __func__, layout->gen_func);
+                       ret = -ENOTSUPP;
+                       goto out;
+               }
+       }
+
+       ret = extract_attr_from_ios(ios, &attrs[2]);
+       if (ret) {
+               EXOFS_ERR("%s: extract_attr of inode_data failed\n", __func__);
                goto out;
        }
-       *obj_size = get_unaligned_be64(attrs[1].val_ptr);
+       if (attrs[2].len) {
+               layout = attrs[2].val_ptr;
+               if (layout->gen_func != cpu_to_le16(LAYOUT_MOVING_WINDOW)) {
+                       EXOFS_ERR("%s: unsupported meta-data layout %d\n",
+                               __func__, layout->gen_func);
+                       ret = -ENOTSUPP;
+                       goto out;
+               }
+       }
 
 out:
        exofs_put_io_state(ios);
@@ -925,7 +972,6 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
        struct exofs_i_info *oi;
        struct exofs_fcb fcb;
        struct inode *inode;
-       uint64_t obj_size;
        int ret;
 
        inode = iget_locked(sb, ino);
@@ -937,7 +983,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
        __oi_init(oi);
 
        /* read the inode from the osd */
-       ret = exofs_get_inode(sb, oi, &fcb, &obj_size);
+       ret = exofs_get_inode(sb, oi, &fcb);
        if (ret)
                goto bad_inode;
 
@@ -958,13 +1004,6 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
        inode->i_blkbits = EXOFS_BLKSHIFT;
        inode->i_generation = le32_to_cpu(fcb.i_generation);
 
-       if ((inode->i_size != obj_size) &&
-               (!exofs_inode_is_fast_symlink(inode))) {
-               EXOFS_ERR("WARNING: Size of inode=%llu != object=%llu\n",
-                         inode->i_size, _LLU(obj_size));
-               /* FIXME: call exofs_inode_recovery() */
-       }
-
        oi->i_dir_start_lookup = 0;
 
        if ((inode->i_nlink == 0) && (inode->i_mode == 0)) {
@@ -1043,7 +1082,7 @@ static void create_done(struct exofs_io_state *ios, void *p)
 
        if (unlikely(ret)) {
                EXOFS_ERR("object=0x%llx creation faild in pid=0x%llx",
-                         _LLU(exofs_oi_objno(oi)), _LLU(sbi->s_pid));
+                         _LLU(exofs_oi_objno(oi)), _LLU(sbi->layout.s_pid));
                /*TODO: When FS is corrupted creation can fail, object already
                 * exist. Get rid of this asynchronous creation, if exist
                 * increment the obj counter and try the next object. Until we
@@ -1104,7 +1143,7 @@ struct inode *exofs_new_inode(struct inode *dir, int mode)
 
        mark_inode_dirty(inode);
 
-       ret = exofs_get_io_state(sbi, &ios);
+       ret = exofs_get_io_state(&sbi->layout, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("exofs_new_inode: exofs_get_io_state failed\n");
                return ERR_PTR(ret);
@@ -1170,8 +1209,10 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
        int ret;
 
        args = kzalloc(sizeof(*args), GFP_KERNEL);
-       if (!args)
+       if (!args) {
+               EXOFS_DBGMSG("Faild kzalloc of args\n");
                return -ENOMEM;
+       }
 
        fcb = &args->fcb;
 
@@ -1200,7 +1241,7 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
        } else
                memcpy(fcb->i_data, oi->i_data, sizeof(fcb->i_data));
 
-       ret = exofs_get_io_state(sbi, &ios);
+       ret = exofs_get_io_state(&sbi->layout, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
                goto free_args;
@@ -1234,13 +1275,14 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
 free_args:
        kfree(args);
 out:
-       EXOFS_DBGMSG("ret=>%d\n", ret);
+       EXOFS_DBGMSG("(0x%lx) do_sync=%d ret=>%d\n",
+                    inode->i_ino, do_sync, ret);
        return ret;
 }
 
-int exofs_write_inode(struct inode *inode, int wait)
+int exofs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
-       return exofs_update_inode(inode, wait);
+       return exofs_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
 }
 
 /*
@@ -1283,7 +1325,7 @@ void exofs_delete_inode(struct inode *inode)
 
        clear_inode(inode);
 
-       ret = exofs_get_io_state(sbi, &ios);
+       ret = exofs_get_io_state(&sbi->layout, &ios);
        if (unlikely(ret)) {
                EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__);
                return;
index 5bad01fa1f9fad6cbe74b2afe500c14c95221a57..5293bc411d17735fcfed8b685e7d3ab9d0e80da4 100644 (file)
  */
 
 #include <scsi/scsi_device.h>
+#include <asm/div64.h>
 
 #include "exofs.h"
 
+#define EXOFS_DBGMSG2(M...) do {} while (0)
+/* #define EXOFS_DBGMSG2 EXOFS_DBGMSG */
+
 void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], const struct osd_obj_id *obj)
 {
        osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
@@ -64,21 +68,24 @@ out:
        return ret;
 }
 
-int exofs_get_io_state(struct exofs_sb_info *sbi, struct exofs_io_state** pios)
+int exofs_get_io_state(struct exofs_layout *layout,
+                      struct exofs_io_state **pios)
 {
        struct exofs_io_state *ios;
 
        /*TODO: Maybe use kmem_cach per sbi of size
-        * exofs_io_state_size(sbi->s_numdevs)
+        * exofs_io_state_size(layout->s_numdevs)
         */
-       ios = kzalloc(exofs_io_state_size(sbi->s_numdevs), GFP_KERNEL);
+       ios = kzalloc(exofs_io_state_size(layout->s_numdevs), GFP_KERNEL);
        if (unlikely(!ios)) {
+               EXOFS_DBGMSG("Faild kzalloc bytes=%d\n",
+                            exofs_io_state_size(layout->s_numdevs));
                *pios = NULL;
                return -ENOMEM;
        }
 
-       ios->sbi = sbi;
-       ios->obj.partition = sbi->s_pid;
+       ios->layout = layout;
+       ios->obj.partition = layout->s_pid;
        *pios = ios;
        return 0;
 }
@@ -101,6 +108,29 @@ void exofs_put_io_state(struct exofs_io_state *ios)
        }
 }
 
+unsigned exofs_layout_od_id(struct exofs_layout *layout,
+                           osd_id obj_no, unsigned layout_index)
+{
+/*     switch (layout->lay_func) {
+       case LAYOUT_MOVING_WINDOW:
+       {*/
+               unsigned dev_mod = obj_no;
+
+               return (layout_index + dev_mod * layout->mirrors_p1) %
+                                                             layout->s_numdevs;
+/*     }
+       case LAYOUT_FUNC_IMPLICT:
+               return layout->devs[layout_index];
+       }*/
+}
+
+static inline struct osd_dev *exofs_ios_od(struct exofs_io_state *ios,
+                                          unsigned layout_index)
+{
+       return ios->layout->s_ods[
+               exofs_layout_od_id(ios->layout, ios->obj.id, layout_index)];
+}
+
 static void _sync_done(struct exofs_io_state *ios, void *p)
 {
        struct completion *waiting = p;
@@ -168,6 +198,21 @@ static int exofs_io_execute(struct exofs_io_state *ios)
        return ret;
 }
 
+static void _clear_bio(struct bio *bio)
+{
+       struct bio_vec *bv;
+       unsigned i;
+
+       __bio_for_each_segment(bv, bio, i, 0) {
+               unsigned this_count = bv->bv_len;
+
+               if (likely(PAGE_SIZE == this_count))
+                       clear_highpage(bv->bv_page);
+               else
+                       zero_user(bv->bv_page, bv->bv_offset, this_count);
+       }
+}
+
 int exofs_check_io(struct exofs_io_state *ios, u64 *resid)
 {
        enum osd_err_priority acumulated_osd_err = 0;
@@ -176,16 +221,25 @@ int exofs_check_io(struct exofs_io_state *ios, u64 *resid)
 
        for (i = 0; i < ios->numdevs; i++) {
                struct osd_sense_info osi;
-               int ret = osd_req_decode_sense(ios->per_dev[i].or, &osi);
+               struct osd_request *or = ios->per_dev[i].or;
+               int ret;
+
+               if (unlikely(!or))
+                       continue;
 
+               ret = osd_req_decode_sense(or, &osi);
                if (likely(!ret))
                        continue;
 
-               if (unlikely(ret == -EFAULT)) {
-                       EXOFS_DBGMSG("%s: EFAULT Need page clear\n", __func__);
-                       /*FIXME: All the pages in this device range should:
-                        *      clear_highpage(page);
-                        */
+               if (OSD_ERR_PRI_CLEAR_PAGES == osi.osd_err_pri) {
+                       /* start read offset passed endof file */
+                       _clear_bio(ios->per_dev[i].bio);
+                       EXOFS_DBGMSG("start read offset passed end of file "
+                               "offset=0x%llx, length=0x%llx\n",
+                               _LLU(ios->per_dev[i].offset),
+                               _LLU(ios->per_dev[i].length));
+
+                       continue; /* we recovered */
                }
 
                if (osi.osd_err_pri >= acumulated_osd_err) {
@@ -205,14 +259,259 @@ int exofs_check_io(struct exofs_io_state *ios, u64 *resid)
        return acumulated_lin_err;
 }
 
+/*
+ * L - logical offset into the file
+ *
+ * U - The number of bytes in a stripe within a group
+ *
+ *     U = stripe_unit * group_width
+ *
+ * T - The number of bytes striped within a group of component objects
+ *     (before advancing to the next group)
+ *
+ *     T = stripe_unit * group_width * group_depth
+ *
+ * S - The number of bytes striped across all component objects
+ *     before the pattern repeats
+ *
+ *     S = stripe_unit * group_width * group_depth * group_count
+ *
+ * M - The "major" (i.e., across all components) stripe number
+ *
+ *     M = L / S
+ *
+ * G - Counts the groups from the beginning of the major stripe
+ *
+ *     G = (L - (M * S)) / T   [or (L % S) / T]
+ *
+ * H - The byte offset within the group
+ *
+ *     H = (L - (M * S)) % T   [or (L % S) % T]
+ *
+ * N - The "minor" (i.e., across the group) stripe number
+ *
+ *     N = H / U
+ *
+ * C - The component index coresponding to L
+ *
+ *     C = (H - (N * U)) / stripe_unit + G * group_width
+ *     [or (L % U) / stripe_unit + G * group_width]
+ *
+ * O - The component offset coresponding to L
+ *
+ *     O = L % stripe_unit + N * stripe_unit + M * group_depth * stripe_unit
+ */
+struct _striping_info {
+       u64 obj_offset;
+       u64 group_length;
+       u64 total_group_length;
+       u64 Major;
+       unsigned dev;
+       unsigned unit_off;
+};
+
+static void _calc_stripe_info(struct exofs_io_state *ios, u64 file_offset,
+                             struct _striping_info *si)
+{
+       u32     stripe_unit = ios->layout->stripe_unit;
+       u32     group_width = ios->layout->group_width;
+       u64     group_depth = ios->layout->group_depth;
+
+       u32     U = stripe_unit * group_width;
+       u64     T = U * group_depth;
+       u64     S = T * ios->layout->group_count;
+       u64     M = div64_u64(file_offset, S);
+
+       /*
+       G = (L - (M * S)) / T
+       H = (L - (M * S)) % T
+       */
+       u64     LmodS = file_offset - M * S;
+       u32     G = div64_u64(LmodS, T);
+       u64     H = LmodS - G * T;
+
+       u32     N = div_u64(H, U);
+
+       /* "H - (N * U)" is just "H % U" so it's bound to u32 */
+       si->dev = (u32)(H - (N * U)) / stripe_unit + G * group_width;
+       si->dev *= ios->layout->mirrors_p1;
+
+       div_u64_rem(file_offset, stripe_unit, &si->unit_off);
+
+       si->obj_offset = si->unit_off + (N * stripe_unit) +
+                                 (M * group_depth * stripe_unit);
+
+       si->group_length = T - H;
+       si->total_group_length = T;
+       si->Major = M;
+}
+
+static int _add_stripe_unit(struct exofs_io_state *ios,  unsigned *cur_pg,
+               unsigned pgbase, struct exofs_per_dev_state *per_dev,
+               int cur_len)
+{
+       unsigned pg = *cur_pg;
+       struct request_queue *q =
+                       osd_request_queue(exofs_ios_od(ios, per_dev->dev));
+
+       per_dev->length += cur_len;
+
+       if (per_dev->bio == NULL) {
+               unsigned pages_in_stripe = ios->layout->group_width *
+                                       (ios->layout->stripe_unit / PAGE_SIZE);
+               unsigned bio_size = (ios->nr_pages + pages_in_stripe) /
+                                               ios->layout->group_width;
+
+               per_dev->bio = bio_kmalloc(GFP_KERNEL, bio_size);
+               if (unlikely(!per_dev->bio)) {
+                       EXOFS_DBGMSG("Faild to allocate BIO size=%u\n",
+                                    bio_size);
+                       return -ENOMEM;
+               }
+       }
+
+       while (cur_len > 0) {
+               unsigned pglen = min_t(unsigned, PAGE_SIZE - pgbase, cur_len);
+               unsigned added_len;
+
+               BUG_ON(ios->nr_pages <= pg);
+               cur_len -= pglen;
+
+               added_len = bio_add_pc_page(q, per_dev->bio, ios->pages[pg],
+                                           pglen, pgbase);
+               if (unlikely(pglen != added_len))
+                       return -ENOMEM;
+               pgbase = 0;
+               ++pg;
+       }
+       BUG_ON(cur_len);
+
+       *cur_pg = pg;
+       return 0;
+}
+
+static int _prepare_one_group(struct exofs_io_state *ios, u64 length,
+                             struct _striping_info *si, unsigned first_comp)
+{
+       unsigned stripe_unit = ios->layout->stripe_unit;
+       unsigned mirrors_p1 = ios->layout->mirrors_p1;
+       unsigned devs_in_group = ios->layout->group_width * mirrors_p1;
+       unsigned dev = si->dev;
+       unsigned first_dev = dev - (dev % devs_in_group);
+       unsigned comp = first_comp + (dev - first_dev);
+       unsigned max_comp = ios->numdevs ? ios->numdevs - mirrors_p1 : 0;
+       unsigned cur_pg = ios->pages_consumed;
+       int ret = 0;
+
+       while (length) {
+               struct exofs_per_dev_state *per_dev = &ios->per_dev[comp];
+               unsigned cur_len, page_off = 0;
+
+               if (!per_dev->length) {
+                       per_dev->dev = dev;
+                       if (dev < si->dev) {
+                               per_dev->offset = si->obj_offset + stripe_unit -
+                                                                  si->unit_off;
+                               cur_len = stripe_unit;
+                       } else if (dev == si->dev) {
+                               per_dev->offset = si->obj_offset;
+                               cur_len = stripe_unit - si->unit_off;
+                               page_off = si->unit_off & ~PAGE_MASK;
+                               BUG_ON(page_off && (page_off != ios->pgbase));
+                       } else { /* dev > si->dev */
+                               per_dev->offset = si->obj_offset - si->unit_off;
+                               cur_len = stripe_unit;
+                       }
+
+                       if (max_comp < comp)
+                               max_comp = comp;
+
+                       dev += mirrors_p1;
+                       dev = (dev % devs_in_group) + first_dev;
+               } else {
+                       cur_len = stripe_unit;
+               }
+               if (cur_len >= length)
+                       cur_len = length;
+
+               ret = _add_stripe_unit(ios, &cur_pg, page_off , per_dev,
+                                      cur_len);
+               if (unlikely(ret))
+                       goto out;
+
+               comp += mirrors_p1;
+               comp = (comp % devs_in_group) + first_comp;
+
+               length -= cur_len;
+       }
+out:
+       ios->numdevs = max_comp + mirrors_p1;
+       ios->pages_consumed = cur_pg;
+       return ret;
+}
+
+static int _prepare_for_striping(struct exofs_io_state *ios)
+{
+       u64 length = ios->length;
+       struct _striping_info si;
+       unsigned devs_in_group = ios->layout->group_width *
+                                ios->layout->mirrors_p1;
+       unsigned first_comp = 0;
+       int ret = 0;
+
+       _calc_stripe_info(ios, ios->offset, &si);
+
+       if (!ios->pages) {
+               if (ios->kern_buff) {
+                       struct exofs_per_dev_state *per_dev = &ios->per_dev[0];
+
+                       per_dev->offset = si.obj_offset;
+                       per_dev->dev = si.dev;
+
+                       /* no cross device without page array */
+                       BUG_ON((ios->layout->group_width > 1) &&
+                              (si.unit_off + ios->length >
+                               ios->layout->stripe_unit));
+               }
+               ios->numdevs = ios->layout->mirrors_p1;
+               return 0;
+       }
+
+       while (length) {
+               if (length < si.group_length)
+                       si.group_length = length;
+
+               ret = _prepare_one_group(ios, si.group_length, &si, first_comp);
+               if (unlikely(ret))
+                       goto out;
+
+               length -= si.group_length;
+
+               si.group_length = si.total_group_length;
+               si.unit_off = 0;
+               ++si.Major;
+               si.obj_offset = si.Major * ios->layout->stripe_unit *
+                                               ios->layout->group_depth;
+
+               si.dev = (si.dev - (si.dev % devs_in_group)) + devs_in_group;
+               si.dev %= ios->layout->s_numdevs;
+
+               first_comp += devs_in_group;
+               first_comp %= ios->layout->s_numdevs;
+       }
+
+out:
+       return ret;
+}
+
 int exofs_sbi_create(struct exofs_io_state *ios)
 {
        int i, ret;
 
-       for (i = 0; i < ios->sbi->s_numdevs; i++) {
+       for (i = 0; i < ios->layout->s_numdevs; i++) {
                struct osd_request *or;
 
-               or = osd_start_request(ios->sbi->s_ods[i], GFP_KERNEL);
+               or = osd_start_request(exofs_ios_od(ios, i), GFP_KERNEL);
                if (unlikely(!or)) {
                        EXOFS_ERR("%s: osd_start_request failed\n", __func__);
                        ret = -ENOMEM;
@@ -233,10 +532,10 @@ int exofs_sbi_remove(struct exofs_io_state *ios)
 {
        int i, ret;
 
-       for (i = 0; i < ios->sbi->s_numdevs; i++) {
+       for (i = 0; i < ios->layout->s_numdevs; i++) {
                struct osd_request *or;
 
-               or = osd_start_request(ios->sbi->s_ods[i], GFP_KERNEL);
+               or = osd_start_request(exofs_ios_od(ios, i), GFP_KERNEL);
                if (unlikely(!or)) {
                        EXOFS_ERR("%s: osd_start_request failed\n", __func__);
                        ret = -ENOMEM;
@@ -253,51 +552,74 @@ out:
        return ret;
 }
 
-int exofs_sbi_write(struct exofs_io_state *ios)
+static int _sbi_write_mirror(struct exofs_io_state *ios, int cur_comp)
 {
-       int i, ret;
+       struct exofs_per_dev_state *master_dev = &ios->per_dev[cur_comp];
+       unsigned dev = ios->per_dev[cur_comp].dev;
+       unsigned last_comp = cur_comp + ios->layout->mirrors_p1;
+       int ret = 0;
 
-       for (i = 0; i < ios->sbi->s_numdevs; i++) {
+       if (ios->pages && !master_dev->length)
+               return 0; /* Just an empty slot */
+
+       for (; cur_comp < last_comp; ++cur_comp, ++dev) {
+               struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp];
                struct osd_request *or;
 
-               or = osd_start_request(ios->sbi->s_ods[i], GFP_KERNEL);
+               or = osd_start_request(exofs_ios_od(ios, dev), GFP_KERNEL);
                if (unlikely(!or)) {
                        EXOFS_ERR("%s: osd_start_request failed\n", __func__);
                        ret = -ENOMEM;
                        goto out;
                }
-               ios->per_dev[i].or = or;
-               ios->numdevs++;
+               per_dev->or = or;
+               per_dev->offset = master_dev->offset;
 
-               if (ios->bio) {
+               if (ios->pages) {
                        struct bio *bio;
 
-                       if (i != 0) {
+                       if (per_dev != master_dev) {
                                bio = bio_kmalloc(GFP_KERNEL,
-                                                 ios->bio->bi_max_vecs);
+                                                 master_dev->bio->bi_max_vecs);
                                if (unlikely(!bio)) {
+                                       EXOFS_DBGMSG(
+                                             "Faild to allocate BIO size=%u\n",
+                                             master_dev->bio->bi_max_vecs);
                                        ret = -ENOMEM;
                                        goto out;
                                }
 
-                               __bio_clone(bio, ios->bio);
+                               __bio_clone(bio, master_dev->bio);
                                bio->bi_bdev = NULL;
                                bio->bi_next = NULL;
-                               ios->per_dev[i].bio =  bio;
+                               per_dev->length = master_dev->length;
+                               per_dev->bio =  bio;
+                               per_dev->dev = dev;
                        } else {
-                               bio = ios->bio;
+                               bio = master_dev->bio;
+                               /* FIXME: bio_set_dir() */
+                               bio->bi_rw |= (1 << BIO_RW);
                        }
 
-                       osd_req_write(or, &ios->obj, ios->offset, bio,
-                                     ios->length);
-/*                     EXOFS_DBGMSG("write sync=%d\n", sync);*/
+                       osd_req_write(or, &ios->obj, per_dev->offset, bio,
+                                     per_dev->length);
+                       EXOFS_DBGMSG("write(0x%llx) offset=0x%llx "
+                                     "length=0x%llx dev=%d\n",
+                                    _LLU(ios->obj.id), _LLU(per_dev->offset),
+                                    _LLU(per_dev->length), dev);
                } else if (ios->kern_buff) {
-                       osd_req_write_kern(or, &ios->obj, ios->offset,
+                       ret = osd_req_write_kern(or, &ios->obj, per_dev->offset,
                                           ios->kern_buff, ios->length);
-/*                     EXOFS_DBGMSG("write_kern sync=%d\n", sync);*/
+                       if (unlikely(ret))
+                               goto out;
+                       EXOFS_DBGMSG2("write_kern(0x%llx) offset=0x%llx "
+                                     "length=0x%llx dev=%d\n",
+                                    _LLU(ios->obj.id), _LLU(per_dev->offset),
+                                    _LLU(ios->length), dev);
                } else {
                        osd_req_set_attributes(or, &ios->obj);
-/*                     EXOFS_DBGMSG("set_attributes sync=%d\n", sync);*/
+                       EXOFS_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n",
+                                    _LLU(ios->obj.id), ios->out_attr_len, dev);
                }
 
                if (ios->out_attr)
@@ -308,54 +630,93 @@ int exofs_sbi_write(struct exofs_io_state *ios)
                        osd_req_add_get_attr_list(or, ios->in_attr,
                                                  ios->in_attr_len);
        }
-       ret = exofs_io_execute(ios);
 
 out:
        return ret;
 }
 
-int exofs_sbi_read(struct exofs_io_state *ios)
+int exofs_sbi_write(struct exofs_io_state *ios)
 {
-       int i, ret;
+       int i;
+       int ret;
 
-       for (i = 0; i < 1; i++) {
-               struct osd_request *or;
-               unsigned first_dev = (unsigned)ios->obj.id;
+       ret = _prepare_for_striping(ios);
+       if (unlikely(ret))
+               return ret;
 
-               first_dev %= ios->sbi->s_numdevs;
-               or = osd_start_request(ios->sbi->s_ods[first_dev], GFP_KERNEL);
-               if (unlikely(!or)) {
-                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               ios->per_dev[i].or = or;
-               ios->numdevs++;
+       for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) {
+               ret = _sbi_write_mirror(ios, i);
+               if (unlikely(ret))
+                       return ret;
+       }
 
-               if (ios->bio) {
-                       osd_req_read(or, &ios->obj, ios->offset, ios->bio,
-                                    ios->length);
-/*                     EXOFS_DBGMSG("read sync=%d\n", sync);*/
-               } else if (ios->kern_buff) {
-                       osd_req_read_kern(or, &ios->obj, ios->offset,
-                                          ios->kern_buff, ios->length);
-/*                     EXOFS_DBGMSG("read_kern sync=%d\n", sync);*/
-               } else {
-                       osd_req_get_attributes(or, &ios->obj);
-/*                     EXOFS_DBGMSG("get_attributes sync=%d\n", sync);*/
-               }
+       ret = exofs_io_execute(ios);
+       return ret;
+}
 
-               if (ios->out_attr)
-                       osd_req_add_set_attr_list(or, ios->out_attr,
-                                                 ios->out_attr_len);
+static int _sbi_read_mirror(struct exofs_io_state *ios, unsigned cur_comp)
+{
+       struct osd_request *or;
+       struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp];
+       unsigned first_dev = (unsigned)ios->obj.id;
 
-               if (ios->in_attr)
-                       osd_req_add_get_attr_list(or, ios->in_attr,
-                                                 ios->in_attr_len);
+       if (ios->pages && !per_dev->length)
+               return 0; /* Just an empty slot */
+
+       first_dev = per_dev->dev + first_dev % ios->layout->mirrors_p1;
+       or = osd_start_request(exofs_ios_od(ios, first_dev), GFP_KERNEL);
+       if (unlikely(!or)) {
+               EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+               return -ENOMEM;
        }
-       ret = exofs_io_execute(ios);
+       per_dev->or = or;
+
+       if (ios->pages) {
+               osd_req_read(or, &ios->obj, per_dev->offset,
+                               per_dev->bio, per_dev->length);
+               EXOFS_DBGMSG("read(0x%llx) offset=0x%llx length=0x%llx"
+                            " dev=%d\n", _LLU(ios->obj.id),
+                            _LLU(per_dev->offset), _LLU(per_dev->length),
+                            first_dev);
+       } else if (ios->kern_buff) {
+               int ret = osd_req_read_kern(or, &ios->obj, per_dev->offset,
+                                           ios->kern_buff, ios->length);
+               EXOFS_DBGMSG2("read_kern(0x%llx) offset=0x%llx "
+                             "length=0x%llx dev=%d ret=>%d\n",
+                             _LLU(ios->obj.id), _LLU(per_dev->offset),
+                             _LLU(ios->length), first_dev, ret);
+               if (unlikely(ret))
+                       return ret;
+       } else {
+               osd_req_get_attributes(or, &ios->obj);
+               EXOFS_DBGMSG2("obj(0x%llx) get_attributes=%d dev=%d\n",
+                             _LLU(ios->obj.id), ios->in_attr_len, first_dev);
+       }
+       if (ios->out_attr)
+               osd_req_add_set_attr_list(or, ios->out_attr, ios->out_attr_len);
 
-out:
+       if (ios->in_attr)
+               osd_req_add_get_attr_list(or, ios->in_attr, ios->in_attr_len);
+
+       return 0;
+}
+
+int exofs_sbi_read(struct exofs_io_state *ios)
+{
+       int i;
+       int ret;
+
+       ret = _prepare_for_striping(ios);
+       if (unlikely(ret))
+               return ret;
+
+       for (i = 0; i < ios->numdevs; i += ios->layout->mirrors_p1) {
+               ret = _sbi_read_mirror(ios, i);
+               if (unlikely(ret))
+                       return ret;
+       }
+
+       ret = exofs_io_execute(ios);
        return ret;
 }
 
@@ -380,42 +741,82 @@ int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr)
        return -EIO;
 }
 
+static int _truncate_mirrors(struct exofs_io_state *ios, unsigned cur_comp,
+                            struct osd_attr *attr)
+{
+       int last_comp = cur_comp + ios->layout->mirrors_p1;
+
+       for (; cur_comp < last_comp; ++cur_comp) {
+               struct exofs_per_dev_state *per_dev = &ios->per_dev[cur_comp];
+               struct osd_request *or;
+
+               or = osd_start_request(exofs_ios_od(ios, cur_comp), GFP_KERNEL);
+               if (unlikely(!or)) {
+                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+                       return -ENOMEM;
+               }
+               per_dev->or = or;
+
+               osd_req_set_attributes(or, &ios->obj);
+               osd_req_add_set_attr_list(or, attr, 1);
+       }
+
+       return 0;
+}
+
 int exofs_oi_truncate(struct exofs_i_info *oi, u64 size)
 {
        struct exofs_sb_info *sbi = oi->vfs_inode.i_sb->s_fs_info;
        struct exofs_io_state *ios;
-       struct osd_attr attr;
-       __be64 newsize;
+       struct exofs_trunc_attr {
+               struct osd_attr attr;
+               __be64 newsize;
+       } *size_attrs;
+       struct _striping_info si;
        int i, ret;
 
-       if (exofs_get_io_state(sbi, &ios))
-               return -ENOMEM;
+       ret = exofs_get_io_state(&sbi->layout, &ios);
+       if (unlikely(ret))
+               return ret;
+
+       size_attrs = kcalloc(ios->layout->group_width, sizeof(*size_attrs),
+                            GFP_KERNEL);
+       if (unlikely(!size_attrs)) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        ios->obj.id = exofs_oi_objno(oi);
        ios->cred = oi->i_cred;
 
-       newsize = cpu_to_be64(size);
-       attr = g_attr_logical_length;
-       attr.val_ptr = &newsize;
+       ios->numdevs = ios->layout->s_numdevs;
+       _calc_stripe_info(ios, size, &si);
 
-       for (i = 0; i < sbi->s_numdevs; i++) {
-               struct osd_request *or;
+       for (i = 0; i < ios->layout->group_width; ++i) {
+               struct exofs_trunc_attr *size_attr = &size_attrs[i];
+               u64 obj_size;
 
-               or = osd_start_request(sbi->s_ods[i], GFP_KERNEL);
-               if (unlikely(!or)) {
-                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               ios->per_dev[i].or = or;
-               ios->numdevs++;
+               if (i < si.dev)
+                       obj_size = si.obj_offset +
+                                       ios->layout->stripe_unit - si.unit_off;
+               else if (i == si.dev)
+                       obj_size = si.obj_offset;
+               else /* i > si.dev */
+                       obj_size = si.obj_offset - si.unit_off;
 
-               osd_req_set_attributes(or, &ios->obj);
-               osd_req_add_set_attr_list(or, &attr, 1);
+               size_attr->newsize = cpu_to_be64(obj_size);
+               size_attr->attr = g_attr_logical_length;
+               size_attr->attr.val_ptr = &size_attr->newsize;
+
+               ret = _truncate_mirrors(ios, i * ios->layout->mirrors_p1,
+                                       &size_attr->attr);
+               if (unlikely(ret))
+                       goto out;
        }
        ret = exofs_io_execute(ios);
 
 out:
+       kfree(size_attrs);
        exofs_put_io_state(ios);
        return ret;
 }
index a1d1e77b12eb45caa1a74463e645b36d1d7f2b9b..6cf5e4e84d6162dcb461eb4a0521023e2142c00a 100644 (file)
@@ -210,7 +210,7 @@ int exofs_sync_fs(struct super_block *sb, int wait)
        sbi = sb->s_fs_info;
        fscb = &sbi->s_fscb;
 
-       ret = exofs_get_io_state(sbi, &ios);
+       ret = exofs_get_io_state(&sbi->layout, &ios);
        if (ret)
                goto out;
 
@@ -264,12 +264,12 @@ static void _exofs_print_device(const char *msg, const char *dev_path,
 
 void exofs_free_sbi(struct exofs_sb_info *sbi)
 {
-       while (sbi->s_numdevs) {
-               int i = --sbi->s_numdevs;
-               struct osd_dev *od = sbi->s_ods[i];
+       while (sbi->layout.s_numdevs) {
+               int i = --sbi->layout.s_numdevs;
+               struct osd_dev *od = sbi->layout.s_ods[i];
 
                if (od) {
-                       sbi->s_ods[i] = NULL;
+                       sbi->layout.s_ods[i] = NULL;
                        osduld_put_device(od);
                }
        }
@@ -298,7 +298,8 @@ static void exofs_put_super(struct super_block *sb)
                                  msecs_to_jiffies(100));
        }
 
-       _exofs_print_device("Unmounting", NULL, sbi->s_ods[0], sbi->s_pid);
+       _exofs_print_device("Unmounting", NULL, sbi->layout.s_ods[0],
+                           sbi->layout.s_pid);
 
        exofs_free_sbi(sbi);
        sb->s_fs_info = NULL;
@@ -307,6 +308,8 @@ static void exofs_put_super(struct super_block *sb)
 static int _read_and_match_data_map(struct exofs_sb_info *sbi, unsigned numdevs,
                                    struct exofs_device_table *dt)
 {
+       u64 stripe_length;
+
        sbi->data_map.odm_num_comps   =
                                le32_to_cpu(dt->dt_data_map.cb_num_comps);
        sbi->data_map.odm_stripe_unit =
@@ -320,14 +323,63 @@ static int _read_and_match_data_map(struct exofs_sb_info *sbi, unsigned numdevs,
        sbi->data_map.odm_raid_algorithm  =
                                le32_to_cpu(dt->dt_data_map.cb_raid_algorithm);
 
-/* FIXME: Hard coded mirror only for now. if not so do not mount */
-       if ((sbi->data_map.odm_num_comps != numdevs) ||
-           (sbi->data_map.odm_stripe_unit != EXOFS_BLKSIZE) ||
-           (sbi->data_map.odm_raid_algorithm != PNFS_OSD_RAID_0) ||
-           (sbi->data_map.odm_mirror_cnt != (numdevs - 1)))
+/* FIXME: Only raid0 for now. if not so, do not mount */
+       if (sbi->data_map.odm_num_comps != numdevs) {
+               EXOFS_ERR("odm_num_comps(%u) != numdevs(%u)\n",
+                         sbi->data_map.odm_num_comps, numdevs);
                return -EINVAL;
-       else
-               return 0;
+       }
+       if (sbi->data_map.odm_raid_algorithm != PNFS_OSD_RAID_0) {
+               EXOFS_ERR("Only RAID_0 for now\n");
+               return -EINVAL;
+       }
+       if (0 != (numdevs % (sbi->data_map.odm_mirror_cnt + 1))) {
+               EXOFS_ERR("Data Map wrong, numdevs=%d mirrors=%d\n",
+                         numdevs, sbi->data_map.odm_mirror_cnt);
+               return -EINVAL;
+       }
+
+       if (0 != (sbi->data_map.odm_stripe_unit & ~PAGE_MASK)) {
+               EXOFS_ERR("Stripe Unit(0x%llx)"
+                         " must be Multples of PAGE_SIZE(0x%lx)\n",
+                         _LLU(sbi->data_map.odm_stripe_unit), PAGE_SIZE);
+               return -EINVAL;
+       }
+
+       sbi->layout.stripe_unit = sbi->data_map.odm_stripe_unit;
+       sbi->layout.mirrors_p1 = sbi->data_map.odm_mirror_cnt + 1;
+
+       if (sbi->data_map.odm_group_width) {
+               sbi->layout.group_width = sbi->data_map.odm_group_width;
+               sbi->layout.group_depth = sbi->data_map.odm_group_depth;
+               if (!sbi->layout.group_depth) {
+                       EXOFS_ERR("group_depth == 0 && group_width != 0\n");
+                       return -EINVAL;
+               }
+               sbi->layout.group_count = sbi->data_map.odm_num_comps /
+                                               sbi->layout.mirrors_p1 /
+                                               sbi->data_map.odm_group_width;
+       } else {
+               if (sbi->data_map.odm_group_depth) {
+                       printk(KERN_NOTICE "Warning: group_depth ignored "
+                               "group_width == 0 && group_depth == %d\n",
+                               sbi->data_map.odm_group_depth);
+                       sbi->data_map.odm_group_depth = 0;
+               }
+               sbi->layout.group_width = sbi->data_map.odm_num_comps /
+                                                       sbi->layout.mirrors_p1;
+               sbi->layout.group_depth = -1;
+               sbi->layout.group_count = 1;
+       }
+
+       stripe_length = (u64)sbi->layout.group_width * sbi->layout.stripe_unit;
+       if (stripe_length >= (1ULL << 32)) {
+               EXOFS_ERR("Total Stripe length(0x%llx)"
+                         " >= 32bit is not supported\n", _LLU(stripe_length));
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 /* @odi is valid only as long as @fscb_dev is valid */
@@ -361,7 +413,7 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
 {
        struct exofs_sb_info *sbi = *psbi;
        struct osd_dev *fscb_od;
-       struct osd_obj_id obj = {.partition = sbi->s_pid,
+       struct osd_obj_id obj = {.partition = sbi->layout.s_pid,
                                 .id = EXOFS_DEVTABLE_ID};
        struct exofs_device_table *dt;
        unsigned table_bytes = table_count * sizeof(dt->dt_dev_table[0]) +
@@ -376,9 +428,9 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
                return -ENOMEM;
        }
 
-       fscb_od = sbi->s_ods[0];
-       sbi->s_ods[0] = NULL;
-       sbi->s_numdevs = 0;
+       fscb_od = sbi->layout.s_ods[0];
+       sbi->layout.s_ods[0] = NULL;
+       sbi->layout.s_numdevs = 0;
        ret = exofs_read_kern(fscb_od, sbi->s_cred, &obj, 0, dt, table_bytes);
        if (unlikely(ret)) {
                EXOFS_ERR("ERROR: reading device table\n");
@@ -397,14 +449,15 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
                goto out;
 
        if (likely(numdevs > 1)) {
-               unsigned size = numdevs * sizeof(sbi->s_ods[0]);
+               unsigned size = numdevs * sizeof(sbi->layout.s_ods[0]);
 
                sbi = krealloc(sbi, sizeof(*sbi) + size, GFP_KERNEL);
                if (unlikely(!sbi)) {
                        ret = -ENOMEM;
                        goto out;
                }
-               memset(&sbi->s_ods[1], 0, size - sizeof(sbi->s_ods[0]));
+               memset(&sbi->layout.s_ods[1], 0,
+                      size - sizeof(sbi->layout.s_ods[0]));
                *psbi = sbi;
        }
 
@@ -427,8 +480,8 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
                 * line. We always keep them in device-table order.
                 */
                if (fscb_od && osduld_device_same(fscb_od, &odi)) {
-                       sbi->s_ods[i] = fscb_od;
-                       ++sbi->s_numdevs;
+                       sbi->layout.s_ods[i] = fscb_od;
+                       ++sbi->layout.s_numdevs;
                        fscb_od = NULL;
                        continue;
                }
@@ -441,8 +494,8 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
                        goto out;
                }
 
-               sbi->s_ods[i] = od;
-               ++sbi->s_numdevs;
+               sbi->layout.s_ods[i] = od;
+               ++sbi->layout.s_numdevs;
 
                /* Read the fscb of the other devices to make sure the FS
                 * partition is there.
@@ -499,9 +552,15 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
                goto free_sbi;
        }
 
-       sbi->s_ods[0] = od;
-       sbi->s_numdevs = 1;
-       sbi->s_pid = opts->pid;
+       /* Default layout in case we do not have a device-table */
+       sbi->layout.stripe_unit = PAGE_SIZE;
+       sbi->layout.mirrors_p1 = 1;
+       sbi->layout.group_width = 1;
+       sbi->layout.group_depth = -1;
+       sbi->layout.group_count = 1;
+       sbi->layout.s_ods[0] = od;
+       sbi->layout.s_numdevs = 1;
+       sbi->layout.s_pid = opts->pid;
        sbi->s_timeout = opts->timeout;
 
        /* fill in some other data by hand */
@@ -514,7 +573,7 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_bdev = NULL;
        sb->s_dev = 0;
 
-       obj.partition = sbi->s_pid;
+       obj.partition = sbi->layout.s_pid;
        obj.id = EXOFS_SUPER_ID;
        exofs_make_credential(sbi->s_cred, &obj);
 
@@ -578,13 +637,13 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
                goto free_sbi;
        }
 
-       _exofs_print_device("Mounting", opts->dev_name, sbi->s_ods[0],
-                           sbi->s_pid);
+       _exofs_print_device("Mounting", opts->dev_name, sbi->layout.s_ods[0],
+                           sbi->layout.s_pid);
        return 0;
 
 free_sbi:
        EXOFS_ERR("Unable to mount exofs on %s pid=0x%llx err=%d\n",
-                 opts->dev_name, sbi->s_pid, ret);
+                 opts->dev_name, sbi->layout.s_pid, ret);
        exofs_free_sbi(sbi);
        return ret;
 }
@@ -627,7 +686,7 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf)
        uint8_t cred_a[OSD_CAP_LEN];
        int ret;
 
-       ret = exofs_get_io_state(sbi, &ios);
+       ret = exofs_get_io_state(&sbi->layout, &ios);
        if (ret) {
                EXOFS_DBGMSG("exofs_get_io_state failed.\n");
                return ret;
index 7f8d2e5a7ea6d2dd8b0ec80d158bab82279408b1..1d081f0cfec2ce13dac7422ad514aaf92c44969e 100644 (file)
@@ -570,7 +570,7 @@ do_more:
 error_return:
        brelse(bitmap_bh);
        release_blocks(sb, freed);
-       vfs_dq_free_block(inode, freed);
+       dquot_free_block(inode, freed);
 }
 
 /**
@@ -1236,6 +1236,7 @@ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal,
        unsigned short windowsz = 0;
        unsigned long ngroups;
        unsigned long num = *count;
+       int ret;
 
        *errp = -ENOSPC;
        sb = inode->i_sb;
@@ -1247,8 +1248,9 @@ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal,
        /*
         * Check quota for allocation of this block.
         */
-       if (vfs_dq_alloc_block(inode, num)) {
-               *errp = -EDQUOT;
+       ret = dquot_alloc_block(inode, num);
+       if (ret) {
+               *errp = ret;
                return 0;
        }
 
@@ -1409,7 +1411,7 @@ allocated:
 
        *errp = 0;
        brelse(bitmap_bh);
-       vfs_dq_free_block(inode, *count-num);
+       dquot_free_block(inode, *count-num);
        *count = num;
        return ret_block;
 
@@ -1420,7 +1422,7 @@ out:
         * Undo the block allocation
         */
        if (!performed_allocation)
-               vfs_dq_free_block(inode, *count);
+               dquot_free_block(inode, *count);
        brelse(bitmap_bh);
        return 0;
 }
index 061914add3cffe038f3f2d3d86e2214cf8df3ea9..0b038e47ad2fa1b86f88c04768c7ebab1cb78a09 100644 (file)
@@ -118,7 +118,7 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
 
 /* inode.c */
 extern struct inode *ext2_iget (struct super_block *, unsigned long);
-extern int ext2_write_inode (struct inode *, int);
+extern int ext2_write_inode (struct inode *, struct writeback_control *);
 extern void ext2_delete_inode (struct inode *);
 extern int ext2_sync_inode (struct inode *);
 extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
index 586e3589d4c2c4f2c4f0de1f1bc4e4fbec0816ed..5d198d0697fbe969d4a5c268374c8d15874ad97e 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/time.h>
 #include <linux/pagemap.h>
+#include <linux/quotaops.h>
 #include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
@@ -70,7 +71,7 @@ const struct file_operations ext2_file_operations = {
        .compat_ioctl   = ext2_compat_ioctl,
 #endif
        .mmap           = generic_file_mmap,
-       .open           = generic_file_open,
+       .open           = dquot_file_open,
        .release        = ext2_release_file,
        .fsync          = ext2_fsync,
        .splice_read    = generic_file_splice_read,
@@ -87,7 +88,7 @@ const struct file_operations ext2_xip_file_operations = {
        .compat_ioctl   = ext2_compat_ioctl,
 #endif
        .mmap           = xip_file_mmap,
-       .open           = generic_file_open,
+       .open           = dquot_file_open,
        .release        = ext2_release_file,
        .fsync          = ext2_fsync,
 };
index 15387c9c17d8f77f36c7eeb053a9fcb779f15598..ad7d572ee8dccf34c152938832ce8ba322a34269 100644 (file)
@@ -121,8 +121,8 @@ void ext2_free_inode (struct inode * inode)
        if (!is_bad_inode(inode)) {
                /* Quota is already initialized in iput() */
                ext2_xattr_delete_inode(inode);
-               vfs_dq_free_inode(inode);
-               vfs_dq_drop(inode);
+               dquot_free_inode(inode);
+               dquot_drop(inode);
        }
 
        es = EXT2_SB(sb)->s_es;
@@ -586,10 +586,10 @@ got:
                goto fail_drop;
        }
 
-       if (vfs_dq_alloc_inode(inode)) {
-               err = -EDQUOT;
+       dquot_initialize(inode);
+       err = dquot_alloc_inode(inode);
+       if (err)
                goto fail_drop;
-       }
 
        err = ext2_init_acl(inode, dir);
        if (err)
@@ -605,10 +605,10 @@ got:
        return inode;
 
 fail_free_drop:
-       vfs_dq_free_inode(inode);
+       dquot_free_inode(inode);
 
 fail_drop:
-       vfs_dq_drop(inode);
+       dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
        inode->i_nlink = 0;
        unlock_new_inode(inode);
index 71b032c65a0220d6e9e6cea12d4621b61acca89b..fc13cc119aad6adcbe7e17fb83284294cfd420ff 100644 (file)
@@ -41,6 +41,8 @@ MODULE_AUTHOR("Remy Card and others");
 MODULE_DESCRIPTION("Second Extended Filesystem");
 MODULE_LICENSE("GPL");
 
+static int __ext2_write_inode(struct inode *inode, int do_sync);
+
 /*
  * Test whether an inode is a fast symlink.
  */
@@ -58,13 +60,15 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode)
  */
 void ext2_delete_inode (struct inode * inode)
 {
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
        truncate_inode_pages(&inode->i_data, 0);
 
        if (is_bad_inode(inode))
                goto no_delete;
        EXT2_I(inode)->i_dtime  = get_seconds();
        mark_inode_dirty(inode);
-       ext2_write_inode(inode, inode_needs_sync(inode));
+       __ext2_write_inode(inode, inode_needs_sync(inode));
 
        inode->i_size = 0;
        if (inode->i_blocks)
@@ -1335,7 +1339,7 @@ bad_inode:
        return ERR_PTR(ret);
 }
 
-int ext2_write_inode(struct inode *inode, int do_sync)
+static int __ext2_write_inode(struct inode *inode, int do_sync)
 {
        struct ext2_inode_info *ei = EXT2_I(inode);
        struct super_block *sb = inode->i_sb;
@@ -1440,6 +1444,11 @@ int ext2_write_inode(struct inode *inode, int do_sync)
        return err;
 }
 
+int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       return __ext2_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
 int ext2_sync_inode(struct inode *inode)
 {
        struct writeback_control wbc = {
@@ -1457,9 +1466,12 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr)
        error = inode_change_ok(inode, iattr);
        if (error)
                return error;
+
+       if (iattr->ia_valid & ATTR_SIZE)
+               dquot_initialize(inode);
        if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
            (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {
-               error = vfs_dq_transfer(inode, iattr) ? -EDQUOT : 0;
+               error = dquot_transfer(inode, iattr);
                if (error)
                        return error;
        }
index dd7175ce56068b18acecc048e88635ca26ef9909..71efb0e9a3f2ababa1ae74033e5caa73332fba61 100644 (file)
@@ -31,6 +31,7 @@
  */
 
 #include <linux/pagemap.h>
+#include <linux/quotaops.h>
 #include "ext2.h"
 #include "xattr.h"
 #include "acl.h"
@@ -99,24 +100,27 @@ struct dentry *ext2_get_parent(struct dentry *child)
  */
 static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
 {
-       struct inode * inode = ext2_new_inode (dir, mode);
-       int err = PTR_ERR(inode);
-       if (!IS_ERR(inode)) {
-               inode->i_op = &ext2_file_inode_operations;
-               if (ext2_use_xip(inode->i_sb)) {
-                       inode->i_mapping->a_ops = &ext2_aops_xip;
-                       inode->i_fop = &ext2_xip_file_operations;
-               } else if (test_opt(inode->i_sb, NOBH)) {
-                       inode->i_mapping->a_ops = &ext2_nobh_aops;
-                       inode->i_fop = &ext2_file_operations;
-               } else {
-                       inode->i_mapping->a_ops = &ext2_aops;
-                       inode->i_fop = &ext2_file_operations;
-               }
-               mark_inode_dirty(inode);
-               err = ext2_add_nondir(dentry, inode);
+       struct inode *inode;
+
+       dquot_initialize(dir);
+
+       inode = ext2_new_inode(dir, mode);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = &ext2_file_inode_operations;
+       if (ext2_use_xip(inode->i_sb)) {
+               inode->i_mapping->a_ops = &ext2_aops_xip;
+               inode->i_fop = &ext2_xip_file_operations;
+       } else if (test_opt(inode->i_sb, NOBH)) {
+               inode->i_mapping->a_ops = &ext2_nobh_aops;
+               inode->i_fop = &ext2_file_operations;
+       } else {
+               inode->i_mapping->a_ops = &ext2_aops;
+               inode->i_fop = &ext2_file_operations;
        }
-       return err;
+       mark_inode_dirty(inode);
+       return ext2_add_nondir(dentry, inode);
 }
 
 static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)
@@ -127,6 +131,8 @@ static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
+       dquot_initialize(dir);
+
        inode = ext2_new_inode (dir, mode);
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
@@ -151,6 +157,8 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
        if (l > sb->s_blocksize)
                goto out;
 
+       dquot_initialize(dir);
+
        inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO);
        err = PTR_ERR(inode);
        if (IS_ERR(inode))
@@ -194,6 +202,8 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir,
        if (inode->i_nlink >= EXT2_LINK_MAX)
                return -EMLINK;
 
+       dquot_initialize(dir);
+
        inode->i_ctime = CURRENT_TIME_SEC;
        inode_inc_link_count(inode);
        atomic_inc(&inode->i_count);
@@ -216,6 +226,8 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        if (dir->i_nlink >= EXT2_LINK_MAX)
                goto out;
 
+       dquot_initialize(dir);
+
        inode_inc_link_count(dir);
 
        inode = ext2_new_inode (dir, S_IFDIR | mode);
@@ -262,6 +274,8 @@ static int ext2_unlink(struct inode * dir, struct dentry *dentry)
        struct page * page;
        int err = -ENOENT;
 
+       dquot_initialize(dir);
+
        de = ext2_find_entry (dir, &dentry->d_name, &page);
        if (!de)
                goto out;
@@ -304,6 +318,9 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
        struct ext2_dir_entry_2 * old_de;
        int err = -ENOENT;
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page);
        if (!old_de)
                goto out;
index f9cb54a585ce8298e7b2fb9bf90169f8323de289..42e4a303b6758628aa87c23d18acbcb6e13110c5 100644 (file)
@@ -194,6 +194,8 @@ static void destroy_inodecache(void)
 static void ext2_clear_inode(struct inode *inode)
 {
        struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info;
+
+       dquot_drop(inode);
        ext2_discard_reservation(inode);
        EXT2_I(inode)->i_block_alloc_info = NULL;
        if (unlikely(rsv))
index 904f00642f84456496114eb1aad86ef7974cee6b..e44dc92609be86eff6638fe6aaae3bf6ecf1e779 100644 (file)
@@ -644,8 +644,8 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
                                   the inode.  */
                                ea_bdebug(new_bh, "reusing block");
 
-                               error = -EDQUOT;
-                               if (vfs_dq_alloc_block(inode, 1)) {
+                               error = dquot_alloc_block(inode, 1);
+                               if (error) {
                                        unlock_buffer(new_bh);
                                        goto cleanup;
                                }
@@ -702,7 +702,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
                 * as if nothing happened and cleanup the unused block */
                if (error && error != -ENOSPC) {
                        if (new_bh && new_bh != old_bh)
-                               vfs_dq_free_block(inode, 1);
+                               dquot_free_block(inode, 1);
                        goto cleanup;
                }
        } else
@@ -734,7 +734,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
                        le32_add_cpu(&HDR(old_bh)->h_refcount, -1);
                        if (ce)
                                mb_cache_entry_release(ce);
-                       vfs_dq_free_block(inode, 1);
+                       dquot_free_block(inode, 1);
                        mark_buffer_dirty(old_bh);
                        ea_bdebug(old_bh, "refcount now=%d",
                                le32_to_cpu(HDR(old_bh)->h_refcount));
@@ -797,7 +797,7 @@ ext2_xattr_delete_inode(struct inode *inode)
                mark_buffer_dirty(bh);
                if (IS_SYNC(inode))
                        sync_dirty_buffer(bh);
-               vfs_dq_free_block(inode, 1);
+               dquot_free_block(inode, 1);
        }
        EXT2_I(inode)->i_file_acl = 0;
 
index 27967f92e8201ca3ce689ff1491a6632319dfc66..161da2d3f890e4f2cb854ee7c16b6feb00d27d4a 100644 (file)
@@ -676,7 +676,7 @@ void ext3_free_blocks(handle_t *handle, struct inode *inode,
        }
        ext3_free_blocks_sb(handle, sb, block, count, &dquot_freed_blocks);
        if (dquot_freed_blocks)
-               vfs_dq_free_block(inode, dquot_freed_blocks);
+               dquot_free_block(inode, dquot_freed_blocks);
        return;
 }
 
@@ -1502,8 +1502,9 @@ ext3_fsblk_t ext3_new_blocks(handle_t *handle, struct inode *inode,
        /*
         * Check quota for allocation of this block.
         */
-       if (vfs_dq_alloc_block(inode, num)) {
-               *errp = -EDQUOT;
+       err = dquot_alloc_block(inode, num);
+       if (err) {
+               *errp = err;
                return 0;
        }
 
@@ -1713,7 +1714,7 @@ allocated:
 
        *errp = 0;
        brelse(bitmap_bh);
-       vfs_dq_free_block(inode, *count-num);
+       dquot_free_block(inode, *count-num);
        *count = num;
        return ret_block;
 
@@ -1728,7 +1729,7 @@ out:
         * Undo the block allocation
         */
        if (!performed_allocation)
-               vfs_dq_free_block(inode, *count);
+               dquot_free_block(inode, *count);
        brelse(bitmap_bh);
        return 0;
 }
index 388bbdfa0b4e0b7e2d6f47ddc3b5665722066895..f55df0e61cbded50f25db7dc094c0ffc5a8dc7a3 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/time.h>
 #include <linux/fs.h>
 #include <linux/jbd.h>
+#include <linux/quotaops.h>
 #include <linux/ext3_fs.h>
 #include <linux/ext3_jbd.h>
 #include "xattr.h"
@@ -33,9 +34,9 @@
  */
 static int ext3_release_file (struct inode * inode, struct file * filp)
 {
-       if (EXT3_I(inode)->i_state & EXT3_STATE_FLUSH_ON_CLOSE) {
+       if (ext3_test_inode_state(inode, EXT3_STATE_FLUSH_ON_CLOSE)) {
                filemap_flush(inode->i_mapping);
-               EXT3_I(inode)->i_state &= ~EXT3_STATE_FLUSH_ON_CLOSE;
+               ext3_clear_inode_state(inode, EXT3_STATE_FLUSH_ON_CLOSE);
        }
        /* if we are the last writer on the inode, drop the block reservation */
        if ((filp->f_mode & FMODE_WRITE) &&
@@ -62,7 +63,7 @@ const struct file_operations ext3_file_operations = {
        .compat_ioctl   = ext3_compat_ioctl,
 #endif
        .mmap           = generic_file_mmap,
-       .open           = generic_file_open,
+       .open           = dquot_file_open,
        .release        = ext3_release_file,
        .fsync          = ext3_sync_file,
        .splice_read    = generic_file_splice_read,
index b3999128513676b008689fd3c319dc1717289dc8..ef9008b885b57b81c757f0e79ddf55d0e520a857 100644 (file)
@@ -123,10 +123,10 @@ void ext3_free_inode (handle_t *handle, struct inode * inode)
         * Note: we must free any quota before locking the superblock,
         * as writing the quota to disk may need the lock as well.
         */
-       vfs_dq_init(inode);
+       dquot_initialize(inode);
        ext3_xattr_delete_inode(handle, inode);
-       vfs_dq_free_inode(inode);
-       vfs_dq_drop(inode);
+       dquot_free_inode(inode);
+       dquot_drop(inode);
 
        is_directory = S_ISDIR(inode->i_mode);
 
@@ -588,10 +588,10 @@ got:
                sizeof(struct ext3_inode) - EXT3_GOOD_OLD_INODE_SIZE : 0;
 
        ret = inode;
-       if (vfs_dq_alloc_inode(inode)) {
-               err = -EDQUOT;
+       dquot_initialize(inode);
+       err = dquot_alloc_inode(inode);
+       if (err)
                goto fail_drop;
-       }
 
        err = ext3_init_acl(handle, inode, dir);
        if (err)
@@ -619,10 +619,10 @@ really_out:
        return ret;
 
 fail_free_drop:
-       vfs_dq_free_inode(inode);
+       dquot_free_inode(inode);
 
 fail_drop:
-       vfs_dq_drop(inode);
+       dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
        inode->i_nlink = 0;
        unlock_new_inode(inode);
index 455e6e6e5cb993912ab0ddae5dd1b6e16d60a7fa..7f920b7263a4056612d05fea439c5b7c93c7cfe8 100644 (file)
@@ -196,6 +196,9 @@ void ext3_delete_inode (struct inode * inode)
 {
        handle_t *handle;
 
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
+
        truncate_inode_pages(&inode->i_data, 0);
 
        if (is_bad_inode(inode))
@@ -1378,7 +1381,7 @@ static int ext3_journalled_write_end(struct file *file,
         */
        if (pos + len > inode->i_size && ext3_can_truncate(inode))
                ext3_orphan_add(handle, inode);
-       EXT3_I(inode)->i_state |= EXT3_STATE_JDATA;
+       ext3_set_inode_state(inode, EXT3_STATE_JDATA);
        if (inode->i_size > EXT3_I(inode)->i_disksize) {
                EXT3_I(inode)->i_disksize = inode->i_size;
                ret2 = ext3_mark_inode_dirty(handle, inode);
@@ -1417,7 +1420,7 @@ static sector_t ext3_bmap(struct address_space *mapping, sector_t block)
        journal_t *journal;
        int err;
 
-       if (EXT3_I(inode)->i_state & EXT3_STATE_JDATA) {
+       if (ext3_test_inode_state(inode, EXT3_STATE_JDATA)) {
                /*
                 * This is a REALLY heavyweight approach, but the use of
                 * bmap on dirty files is expected to be extremely rare:
@@ -1436,7 +1439,7 @@ static sector_t ext3_bmap(struct address_space *mapping, sector_t block)
                 * everything they get.
                 */
 
-               EXT3_I(inode)->i_state &= ~EXT3_STATE_JDATA;
+               ext3_clear_inode_state(inode, EXT3_STATE_JDATA);
                journal = EXT3_JOURNAL(inode);
                journal_lock_updates(journal);
                err = journal_flush(journal);
@@ -1528,6 +1531,7 @@ static int ext3_ordered_writepage(struct page *page,
        int err;
 
        J_ASSERT(PageLocked(page));
+       WARN_ON_ONCE(IS_RDONLY(inode));
 
        /*
         * We give up here if we're reentered, because it might be for a
@@ -1600,6 +1604,9 @@ static int ext3_writeback_writepage(struct page *page,
        int ret = 0;
        int err;
 
+       J_ASSERT(PageLocked(page));
+       WARN_ON_ONCE(IS_RDONLY(inode));
+
        if (ext3_journal_current_handle())
                goto out_fail;
 
@@ -1642,6 +1649,9 @@ static int ext3_journalled_writepage(struct page *page,
        int ret = 0;
        int err;
 
+       J_ASSERT(PageLocked(page));
+       WARN_ON_ONCE(IS_RDONLY(inode));
+
        if (ext3_journal_current_handle())
                goto no_write;
 
@@ -1670,7 +1680,7 @@ static int ext3_journalled_writepage(struct page *page,
                                PAGE_CACHE_SIZE, NULL, write_end_fn);
                if (ret == 0)
                        ret = err;
-               EXT3_I(inode)->i_state |= EXT3_STATE_JDATA;
+               ext3_set_inode_state(inode, EXT3_STATE_JDATA);
                unlock_page(page);
        } else {
                /*
@@ -1785,8 +1795,9 @@ retry:
                handle = ext3_journal_start(inode, 2);
                if (IS_ERR(handle)) {
                        /* This is really bad luck. We've written the data
-                        * but cannot extend i_size. Bail out and pretend
-                        * the write failed... */
+                        * but cannot extend i_size. Truncate allocated blocks
+                        * and pretend the write failed... */
+                       ext3_truncate(inode);
                        ret = PTR_ERR(handle);
                        goto out;
                }
@@ -2402,7 +2413,7 @@ void ext3_truncate(struct inode *inode)
                goto out_notrans;
 
        if (inode->i_size == 0 && ext3_should_writeback_data(inode))
-               ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE;
+               ext3_set_inode_state(inode, EXT3_STATE_FLUSH_ON_CLOSE);
 
        /*
         * We have to lock the EOF page here, because lock_page() nests
@@ -2721,7 +2732,7 @@ int ext3_get_inode_loc(struct inode *inode, struct ext3_iloc *iloc)
 {
        /* We have all inode data except xattrs in memory here. */
        return __ext3_get_inode_loc(inode, iloc,
-               !(EXT3_I(inode)->i_state & EXT3_STATE_XATTR));
+               !ext3_test_inode_state(inode, EXT3_STATE_XATTR));
 }
 
 void ext3_set_inode_flags(struct inode *inode)
@@ -2893,7 +2904,7 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino)
                                        EXT3_GOOD_OLD_INODE_SIZE +
                                        ei->i_extra_isize;
                        if (*magic == cpu_to_le32(EXT3_XATTR_MAGIC))
-                                ei->i_state |= EXT3_STATE_XATTR;
+                                ext3_set_inode_state(inode, EXT3_STATE_XATTR);
                }
        } else
                ei->i_extra_isize = 0;
@@ -2955,7 +2966,7 @@ again:
 
        /* For fields not not tracking in the in-memory inode,
         * initialise them to zero for new inodes. */
-       if (ei->i_state & EXT3_STATE_NEW)
+       if (ext3_test_inode_state(inode, EXT3_STATE_NEW))
                memset(raw_inode, 0, EXT3_SB(inode->i_sb)->s_inode_size);
 
        ext3_get_inode_flags(ei);
@@ -3052,7 +3063,7 @@ again:
        rc = ext3_journal_dirty_metadata(handle, bh);
        if (!err)
                err = rc;
-       ei->i_state &= ~EXT3_STATE_NEW;
+       ext3_clear_inode_state(inode, EXT3_STATE_NEW);
 
        atomic_set(&ei->i_sync_tid, handle->h_transaction->t_tid);
 out_brelse:
@@ -3096,7 +3107,7 @@ out_brelse:
  * `stuff()' is running, and the new i_size will be lost.  Plus the inode
  * will no longer be on the superblock's dirty inode list.
  */
-int ext3_write_inode(struct inode *inode, int wait)
+int ext3_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        if (current->flags & PF_MEMALLOC)
                return 0;
@@ -3107,7 +3118,7 @@ int ext3_write_inode(struct inode *inode, int wait)
                return -EIO;
        }
 
-       if (!wait)
+       if (wbc->sync_mode != WB_SYNC_ALL)
                return 0;
 
        return ext3_force_commit(inode->i_sb);
@@ -3140,6 +3151,8 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
        if (error)
                return error;
 
+       if (ia_valid & ATTR_SIZE)
+               dquot_initialize(inode);
        if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
                (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
                handle_t *handle;
@@ -3152,7 +3165,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
                        error = PTR_ERR(handle);
                        goto err_out;
                }
-               error = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
+               error = dquot_transfer(inode, attr);
                if (error) {
                        ext3_journal_stop(handle);
                        return error;
@@ -3237,7 +3250,7 @@ static int ext3_writepage_trans_blocks(struct inode *inode)
                ret = 2 * (bpp + indirects) + 2;
 
 #ifdef CONFIG_QUOTA
-       /* We know that structure was already allocated during vfs_dq_init so
+       /* We know that structure was already allocated during dquot_initialize so
         * we will be updating only the data blocks + inodes */
        ret += EXT3_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
 #endif
@@ -3328,7 +3341,7 @@ int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode)
  * i_size has been changed by generic_commit_write() and we thus need
  * to include the updated inode in the current transaction.
  *
- * Also, vfs_dq_alloc_space() will always dirty the inode when blocks
+ * Also, dquot_alloc_space() will always dirty the inode when blocks
  * are allocated to the file.
  *
  * If the inode is marked synchronous, we don't honour that here - doing
index 7b0e44f7d66fb839c7c8002fb6e3928b5e3e04a8..ee184084ca427a08d58ec58d8b9266b3263751ca 100644 (file)
@@ -1696,6 +1696,8 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode,
        struct inode * inode;
        int err, retries = 0;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -1730,6 +1732,8 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -1766,6 +1770,8 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        if (dir->i_nlink >= EXT3_LINK_MAX)
                return -EMLINK;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -2060,7 +2066,9 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
 
        /* Initialize quotas before so that eventual writes go in
         * separate transaction */
-       vfs_dq_init(dentry->d_inode);
+       dquot_initialize(dir);
+       dquot_initialize(dentry->d_inode);
+
        handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
@@ -2119,7 +2127,9 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
 
        /* Initialize quotas before so that eventual writes go
         * in separate transaction */
-       vfs_dq_init(dentry->d_inode);
+       dquot_initialize(dir);
+       dquot_initialize(dentry->d_inode);
+
        handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
@@ -2174,6 +2184,8 @@ static int ext3_symlink (struct inode * dir,
        if (l > dir->i_sb->s_blocksize)
                return -ENAMETOOLONG;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
@@ -2228,6 +2240,9 @@ static int ext3_link (struct dentry * old_dentry,
 
        if (inode->i_nlink >= EXT3_LINK_MAX)
                return -EMLINK;
+
+       dquot_initialize(dir);
+
        /*
         * Return -ENOENT if we've raced with unlink and i_nlink is 0.  Doing
         * otherwise has the potential to corrupt the orphan inode list.
@@ -2278,12 +2293,15 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
        struct ext3_dir_entry_2 * old_de, * new_de;
        int retval, flush_file = 0;
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        old_bh = new_bh = dir_bh = NULL;
 
        /* Initialize quotas before so that eventual writes go
         * in separate transaction */
        if (new_dentry->d_inode)
-               vfs_dq_init(new_dentry->d_inode);
+               dquot_initialize(new_dentry->d_inode);
        handle = ext3_journal_start(old_dir, 2 *
                                        EXT3_DATA_TRANS_BLOCKS(old_dir->i_sb) +
                                        EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2);
index afa2b569da101094dee07e58fda0f40391f8e045..e844accbf55d85aab676feda3a8af7a945f7dfa9 100644 (file)
@@ -181,7 +181,7 @@ static void ext3_handle_error(struct super_block *sb)
        if (!test_opt (sb, ERRORS_CONT)) {
                journal_t *journal = EXT3_SB(sb)->s_journal;
 
-               EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT;
+               set_opt(EXT3_SB(sb)->s_mount_opt, ABORT);
                if (journal)
                        journal_abort(journal, -EIO);
        }
@@ -296,7 +296,7 @@ void ext3_abort (struct super_block * sb, const char * function,
                "error: remounting filesystem read-only");
        EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS;
        sb->s_flags |= MS_RDONLY;
-       EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT;
+       set_opt(EXT3_SB(sb)->s_mount_opt, ABORT);
        if (EXT3_SB(sb)->s_journal)
                journal_abort(EXT3_SB(sb)->s_journal, -EIO);
 }
@@ -528,6 +528,8 @@ static void destroy_inodecache(void)
 static void ext3_clear_inode(struct inode *inode)
 {
        struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info;
+
+       dquot_drop(inode);
        ext3_discard_reservation(inode);
        EXT3_I(inode)->i_block_alloc_info = NULL;
        if (unlikely(rsv))
@@ -562,10 +564,10 @@ static inline void ext3_show_quota_options(struct seq_file *seq, struct super_bl
        if (sbi->s_qf_names[GRPQUOTA])
                seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);
 
-       if (sbi->s_mount_opt & EXT3_MOUNT_USRQUOTA)
+       if (test_opt(sb, USRQUOTA))
                seq_puts(seq, ",usrquota");
 
-       if (sbi->s_mount_opt & EXT3_MOUNT_GRPQUOTA)
+       if (test_opt(sb, GRPQUOTA))
                seq_puts(seq, ",grpquota");
 #endif
 }
@@ -656,8 +658,7 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs)
        if (test_opt(sb, NOBH))
                seq_puts(seq, ",nobh");
 
-       seq_printf(seq, ",data=%s", data_mode_string(sbi->s_mount_opt &
-                                                    EXT3_MOUNT_DATA_FLAGS));
+       seq_printf(seq, ",data=%s", data_mode_string(test_opt(sb, DATA_FLAGS)));
        if (test_opt(sb, DATA_ERR_ABORT))
                seq_puts(seq, ",data_err=abort");
 
@@ -751,13 +752,6 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
                                const char *data, size_t len, loff_t off);
 
 static const struct dquot_operations ext3_quota_operations = {
-       .initialize     = dquot_initialize,
-       .drop           = dquot_drop,
-       .alloc_space    = dquot_alloc_space,
-       .alloc_inode    = dquot_alloc_inode,
-       .free_space     = dquot_free_space,
-       .free_inode     = dquot_free_inode,
-       .transfer       = dquot_transfer,
        .write_dquot    = ext3_write_dquot,
        .acquire_dquot  = ext3_acquire_dquot,
        .release_dquot  = ext3_release_dquot,
@@ -896,6 +890,63 @@ static ext3_fsblk_t get_sb_block(void **data, struct super_block *sb)
        return sb_block;
 }
 
+#ifdef CONFIG_QUOTA
+static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
+{
+       struct ext3_sb_info *sbi = EXT3_SB(sb);
+       char *qname;
+
+       if (sb_any_quota_loaded(sb) &&
+               !sbi->s_qf_names[qtype]) {
+               ext3_msg(sb, KERN_ERR,
+                       "Cannot change journaled "
+                       "quota options when quota turned on");
+               return 0;
+       }
+       qname = match_strdup(args);
+       if (!qname) {
+               ext3_msg(sb, KERN_ERR,
+                       "Not enough memory for storing quotafile name");
+               return 0;
+       }
+       if (sbi->s_qf_names[qtype] &&
+               strcmp(sbi->s_qf_names[qtype], qname)) {
+               ext3_msg(sb, KERN_ERR,
+                       "%s quota file already specified", QTYPE2NAME(qtype));
+               kfree(qname);
+               return 0;
+       }
+       sbi->s_qf_names[qtype] = qname;
+       if (strchr(sbi->s_qf_names[qtype], '/')) {
+               ext3_msg(sb, KERN_ERR,
+                       "quotafile must be on filesystem root");
+               kfree(sbi->s_qf_names[qtype]);
+               sbi->s_qf_names[qtype] = NULL;
+               return 0;
+       }
+       set_opt(sbi->s_mount_opt, QUOTA);
+       return 1;
+}
+
+static int clear_qf_name(struct super_block *sb, int qtype) {
+
+       struct ext3_sb_info *sbi = EXT3_SB(sb);
+
+       if (sb_any_quota_loaded(sb) &&
+               sbi->s_qf_names[qtype]) {
+               ext3_msg(sb, KERN_ERR, "Cannot change journaled quota options"
+                       " when quota turned on");
+               return 0;
+       }
+       /*
+        * The space will be released later when all options are confirmed
+        * to be correct
+        */
+       sbi->s_qf_names[qtype] = NULL;
+       return 1;
+}
+#endif
+
 static int parse_options (char *options, struct super_block *sb,
                          unsigned int *inum, unsigned long *journal_devnum,
                          ext3_fsblk_t *n_blocks_count, int is_remount)
@@ -906,8 +957,7 @@ static int parse_options (char *options, struct super_block *sb,
        int data_opt = 0;
        int option;
 #ifdef CONFIG_QUOTA
-       int qtype, qfmt;
-       char *qname;
+       int qfmt;
 #endif
 
        if (!options)
@@ -1065,20 +1115,19 @@ static int parse_options (char *options, struct super_block *sb,
                        data_opt = EXT3_MOUNT_WRITEBACK_DATA;
                datacheck:
                        if (is_remount) {
-                               if ((sbi->s_mount_opt & EXT3_MOUNT_DATA_FLAGS)
-                                               == data_opt)
+                               if (test_opt(sb, DATA_FLAGS) == data_opt)
                                        break;
                                ext3_msg(sb, KERN_ERR,
                                        "error: cannot change "
                                        "data mode on remount. The filesystem "
                                        "is mounted in data=%s mode and you "
                                        "try to remount it in data=%s mode.",
-                                       data_mode_string(sbi->s_mount_opt &
-                                                       EXT3_MOUNT_DATA_FLAGS),
+                                       data_mode_string(test_opt(sb,
+                                                       DATA_FLAGS)),
                                        data_mode_string(data_opt));
                                return 0;
                        } else {
-                               sbi->s_mount_opt &= ~EXT3_MOUNT_DATA_FLAGS;
+                               clear_opt(sbi->s_mount_opt, DATA_FLAGS);
                                sbi->s_mount_opt |= data_opt;
                        }
                        break;
@@ -1090,62 +1139,20 @@ static int parse_options (char *options, struct super_block *sb,
                        break;
 #ifdef CONFIG_QUOTA
                case Opt_usrjquota:
-                       qtype = USRQUOTA;
-                       goto set_qf_name;
-               case Opt_grpjquota:
-                       qtype = GRPQUOTA;
-set_qf_name:
-                       if (sb_any_quota_loaded(sb) &&
-                           !sbi->s_qf_names[qtype]) {
-                               ext3_msg(sb, KERN_ERR,
-                                       "error: cannot change journaled "
-                                       "quota options when quota turned on.");
-                               return 0;
-                       }
-                       qname = match_strdup(&args[0]);
-                       if (!qname) {
-                               ext3_msg(sb, KERN_ERR,
-                                       "error: not enough memory for "
-                                       "storing quotafile name.");
+                       if (!set_qf_name(sb, USRQUOTA, &args[0]))
                                return 0;
-                       }
-                       if (sbi->s_qf_names[qtype] &&
-                           strcmp(sbi->s_qf_names[qtype], qname)) {
-                               ext3_msg(sb, KERN_ERR,
-                                       "error: %s quota file already "
-                                       "specified.", QTYPE2NAME(qtype));
-                               kfree(qname);
-                               return 0;
-                       }
-                       sbi->s_qf_names[qtype] = qname;
-                       if (strchr(sbi->s_qf_names[qtype], '/')) {
-                               ext3_msg(sb, KERN_ERR,
-                                       "error: quotafile must be on "
-                                       "filesystem root.");
-                               kfree(sbi->s_qf_names[qtype]);
-                               sbi->s_qf_names[qtype] = NULL;
+                       break;
+               case Opt_grpjquota:
+                       if (!set_qf_name(sb, GRPQUOTA, &args[0]))
                                return 0;
-                       }
-                       set_opt(sbi->s_mount_opt, QUOTA);
                        break;
                case Opt_offusrjquota:
-                       qtype = USRQUOTA;
-                       goto clear_qf_name;
+                       if (!clear_qf_name(sb, USRQUOTA))
+                               return 0;
+                       break;
                case Opt_offgrpjquota:
-                       qtype = GRPQUOTA;
-clear_qf_name:
-                       if (sb_any_quota_loaded(sb) &&
-                           sbi->s_qf_names[qtype]) {
-                               ext3_msg(sb, KERN_ERR, "error: cannot change "
-                                       "journaled quota options when "
-                                       "quota turned on.");
+                       if (!clear_qf_name(sb, GRPQUOTA))
                                return 0;
-                       }
-                       /*
-                        * The space will be released later when all options
-                        * are confirmed to be correct
-                        */
-                       sbi->s_qf_names[qtype] = NULL;
                        break;
                case Opt_jqfmt_vfsold:
                        qfmt = QFMT_VFS_OLD;
@@ -1244,18 +1251,12 @@ set_qf_format:
        }
 #ifdef CONFIG_QUOTA
        if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
-               if ((sbi->s_mount_opt & EXT3_MOUNT_USRQUOTA) &&
-                    sbi->s_qf_names[USRQUOTA])
+               if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
                        clear_opt(sbi->s_mount_opt, USRQUOTA);
-
-               if ((sbi->s_mount_opt & EXT3_MOUNT_GRPQUOTA) &&
-                    sbi->s_qf_names[GRPQUOTA])
+               if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
                        clear_opt(sbi->s_mount_opt, GRPQUOTA);
 
-               if ((sbi->s_qf_names[USRQUOTA] &&
-                               (sbi->s_mount_opt & EXT3_MOUNT_GRPQUOTA)) ||
-                   (sbi->s_qf_names[GRPQUOTA] &&
-                               (sbi->s_mount_opt & EXT3_MOUNT_USRQUOTA))) {
+               if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
                        ext3_msg(sb, KERN_ERR, "error: old and new quota "
                                        "format mixing.");
                        return 0;
@@ -1478,7 +1479,7 @@ static void ext3_orphan_cleanup (struct super_block * sb,
                }
 
                list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan);
-               vfs_dq_init(inode);
+               dquot_initialize(inode);
                if (inode->i_nlink) {
                        printk(KERN_DEBUG
                                "%s: truncating inode %lu to %Ld bytes\n",
@@ -1671,11 +1672,11 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
                set_opt(sbi->s_mount_opt, POSIX_ACL);
 #endif
        if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_DATA)
-               sbi->s_mount_opt |= EXT3_MOUNT_JOURNAL_DATA;
+               set_opt(sbi->s_mount_opt, JOURNAL_DATA);
        else if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_ORDERED)
-               sbi->s_mount_opt |= EXT3_MOUNT_ORDERED_DATA;
+               set_opt(sbi->s_mount_opt, ORDERED_DATA);
        else if ((def_mount_opts & EXT3_DEFM_JMODE) == EXT3_DEFM_JMODE_WBACK)
-               sbi->s_mount_opt |= EXT3_MOUNT_WRITEBACK_DATA;
+               set_opt(sbi->s_mount_opt, WRITEBACK_DATA);
 
        if (le16_to_cpu(sbi->s_es->s_errors) == EXT3_ERRORS_PANIC)
                set_opt(sbi->s_mount_opt, ERRORS_PANIC);
@@ -1694,7 +1695,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
                goto failed_mount;
 
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
-               ((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+               (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
 
        if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV &&
            (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) ||
@@ -2561,11 +2562,11 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
                goto restore_opts;
        }
 
-       if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
+       if (test_opt(sb, ABORT))
                ext3_abort(sb, __func__, "Abort forced by user");
 
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
-               ((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+               (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
 
        es = sbi->s_es;
 
@@ -2573,7 +2574,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
 
        if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY) ||
                n_blocks_count > le32_to_cpu(es->s_blocks_count)) {
-               if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) {
+               if (test_opt(sb, ABORT)) {
                        err = -EROFS;
                        goto restore_opts;
                }
@@ -2734,7 +2735,7 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf)
  * Process 1                         Process 2
  * ext3_create()                     quota_sync()
  *   journal_start()                   write_dquot()
- *   vfs_dq_init()                       down(dqio_mutex)
+ *   dquot_initialize()                       down(dqio_mutex)
  *     down(dqio_mutex)                    journal_start()
  *
  */
@@ -2942,9 +2943,7 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
        sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
        int err = 0;
        int offset = off & (sb->s_blocksize - 1);
-       int tocopy;
        int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL;
-       size_t towrite = len;
        struct buffer_head *bh;
        handle_t *handle = journal_current_handle();
 
@@ -2955,53 +2954,54 @@ static ssize_t ext3_quota_write(struct super_block *sb, int type,
                        (unsigned long long)off, (unsigned long long)len);
                return -EIO;
        }
+
+       /*
+        * Since we account only one data block in transaction credits,
+        * then it is impossible to cross a block boundary.
+        */
+       if (sb->s_blocksize - offset < len) {
+               ext3_msg(sb, KERN_WARNING, "Quota write (off=%llu, len=%llu)"
+                       " cancelled because not block aligned",
+                       (unsigned long long)off, (unsigned long long)len);
+               return -EIO;
+       }
        mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
-       while (towrite > 0) {
-               tocopy = sb->s_blocksize - offset < towrite ?
-                               sb->s_blocksize - offset : towrite;
-               bh = ext3_bread(handle, inode, blk, 1, &err);
-               if (!bh)
+       bh = ext3_bread(handle, inode, blk, 1, &err);
+       if (!bh)
+               goto out;
+       if (journal_quota) {
+               err = ext3_journal_get_write_access(handle, bh);
+               if (err) {
+                       brelse(bh);
                        goto out;
-               if (journal_quota) {
-                       err = ext3_journal_get_write_access(handle, bh);
-                       if (err) {
-                               brelse(bh);
-                               goto out;
-                       }
-               }
-               lock_buffer(bh);
-               memcpy(bh->b_data+offset, data, tocopy);
-               flush_dcache_page(bh->b_page);
-               unlock_buffer(bh);
-               if (journal_quota)
-                       err = ext3_journal_dirty_metadata(handle, bh);
-               else {
-                       /* Always do at least ordered writes for quotas */
-                       err = ext3_journal_dirty_data(handle, bh);
-                       mark_buffer_dirty(bh);
                }
-               brelse(bh);
-               if (err)
-                       goto out;
-               offset = 0;
-               towrite -= tocopy;
-               data += tocopy;
-               blk++;
        }
+       lock_buffer(bh);
+       memcpy(bh->b_data+offset, data, len);
+       flush_dcache_page(bh->b_page);
+       unlock_buffer(bh);
+       if (journal_quota)
+               err = ext3_journal_dirty_metadata(handle, bh);
+       else {
+               /* Always do at least ordered writes for quotas */
+               err = ext3_journal_dirty_data(handle, bh);
+               mark_buffer_dirty(bh);
+       }
+       brelse(bh);
 out:
-       if (len == towrite) {
+       if (err) {
                mutex_unlock(&inode->i_mutex);
                return err;
        }
-       if (inode->i_size < off+len-towrite) {
-               i_size_write(inode, off+len-towrite);
+       if (inode->i_size < off + len) {
+               i_size_write(inode, off + len);
                EXT3_I(inode)->i_disksize = inode->i_size;
        }
        inode->i_version++;
        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
        ext3_mark_inode_dirty(handle, inode);
        mutex_unlock(&inode->i_mutex);
-       return len - towrite;
+       return len;
 }
 
 #endif
index 66895ccf76c7a26afe8a63ef3d295a316a854d9e..534a94c3a93338a843cd050ef99ee03f9304d4a2 100644 (file)
@@ -274,7 +274,7 @@ ext3_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
        void *end;
        int error;
 
-       if (!(EXT3_I(inode)->i_state & EXT3_STATE_XATTR))
+       if (!ext3_test_inode_state(inode, EXT3_STATE_XATTR))
                return -ENODATA;
        error = ext3_get_inode_loc(inode, &iloc);
        if (error)
@@ -403,7 +403,7 @@ ext3_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
        void *end;
        int error;
 
-       if (!(EXT3_I(inode)->i_state & EXT3_STATE_XATTR))
+       if (!ext3_test_inode_state(inode, EXT3_STATE_XATTR))
                return 0;
        error = ext3_get_inode_loc(inode, &iloc);
        if (error)
@@ -500,7 +500,7 @@ ext3_xattr_release_block(handle_t *handle, struct inode *inode,
                error = ext3_journal_dirty_metadata(handle, bh);
                if (IS_SYNC(inode))
                        handle->h_sync = 1;
-               vfs_dq_free_block(inode, 1);
+               dquot_free_block(inode, 1);
                ea_bdebug(bh, "refcount now=%d; releasing",
                          le32_to_cpu(BHDR(bh)->h_refcount));
                if (ce)
@@ -775,8 +775,8 @@ inserted:
                        else {
                                /* The old block is released after updating
                                   the inode. */
-                               error = -EDQUOT;
-                               if (vfs_dq_alloc_block(inode, 1))
+                               error = dquot_alloc_block(inode, 1);
+                               if (error)
                                        goto cleanup;
                                error = ext3_journal_get_write_access(handle,
                                                                      new_bh);
@@ -850,7 +850,7 @@ cleanup:
        return error;
 
 cleanup_dquot:
-       vfs_dq_free_block(inode, 1);
+       dquot_free_block(inode, 1);
        goto cleanup;
 
 bad_block:
@@ -882,7 +882,7 @@ ext3_xattr_ibody_find(struct inode *inode, struct ext3_xattr_info *i,
        is->s.base = is->s.first = IFIRST(header);
        is->s.here = is->s.first;
        is->s.end = (void *)raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
-       if (EXT3_I(inode)->i_state & EXT3_STATE_XATTR) {
+       if (ext3_test_inode_state(inode, EXT3_STATE_XATTR)) {
                error = ext3_xattr_check_names(IFIRST(header), is->s.end);
                if (error)
                        return error;
@@ -914,10 +914,10 @@ ext3_xattr_ibody_set(handle_t *handle, struct inode *inode,
        header = IHDR(inode, ext3_raw_inode(&is->iloc));
        if (!IS_LAST_ENTRY(s->first)) {
                header->h_magic = cpu_to_le32(EXT3_XATTR_MAGIC);
-               EXT3_I(inode)->i_state |= EXT3_STATE_XATTR;
+               ext3_set_inode_state(inode, EXT3_STATE_XATTR);
        } else {
                header->h_magic = cpu_to_le32(0);
-               EXT3_I(inode)->i_state &= ~EXT3_STATE_XATTR;
+               ext3_clear_inode_state(inode, EXT3_STATE_XATTR);
        }
        return 0;
 }
@@ -967,10 +967,10 @@ ext3_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
        if (error)
                goto cleanup;
 
-       if (EXT3_I(inode)->i_state & EXT3_STATE_NEW) {
+       if (ext3_test_inode_state(inode, EXT3_STATE_NEW)) {
                struct ext3_inode *raw_inode = ext3_raw_inode(&is.iloc);
                memset(raw_inode, 0, EXT3_SB(inode->i_sb)->s_inode_size);
-               EXT3_I(inode)->i_state &= ~EXT3_STATE_NEW;
+               ext3_clear_inode_state(inode, EXT3_STATE_NEW);
        }
 
        error = ext3_xattr_ibody_find(inode, &i, &is);
index 22bc7435d9134b397f537440faa15ce890b7c44f..d2f37a5516c76c68ed5ddc6c47471ef4f16d9df3 100644 (file)
@@ -97,8 +97,8 @@ unsigned 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)) {
-                       ext4_error(sb, __func__,
-                                 "Checksum bad for group %u", block_group);
+                       ext4_error(sb, "Checksum bad for group %u",
+                                       block_group);
                        ext4_free_blks_set(sb, gdp, 0);
                        ext4_free_inodes_set(sb, gdp, 0);
                        ext4_itable_unused_set(sb, gdp, 0);
@@ -130,8 +130,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
                 * to make sure we calculate the right free blocks
                 */
                group_blocks = ext4_blocks_count(sbi->s_es) -
-                       le32_to_cpu(sbi->s_es->s_first_data_block) -
-                       (EXT4_BLOCKS_PER_GROUP(sb) * (ngroups - 1));
+                       ext4_group_first_block_no(sb, ngroups - 1);
        } else {
                group_blocks = EXT4_BLOCKS_PER_GROUP(sb);
        }
@@ -189,9 +188,6 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
  * when a file system is mounted (see ext4_fill_super).
  */
 
-
-#define in_range(b, first, len)        ((b) >= (first) && (b) <= (first) + (len) - 1)
-
 /**
  * ext4_get_group_desc() -- load group descriptor from disk
  * @sb:                        super block
@@ -210,10 +206,8 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
        struct ext4_sb_info *sbi = EXT4_SB(sb);
 
        if (block_group >= ngroups) {
-               ext4_error(sb, "ext4_get_group_desc",
-                          "block_group >= groups_count - "
-                          "block_group = %u, groups_count = %u",
-                          block_group, ngroups);
+               ext4_error(sb, "block_group >= groups_count - block_group = %u,"
+                          " groups_count = %u", block_group, ngroups);
 
                return NULL;
        }
@@ -221,8 +215,7 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
        group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
        offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
        if (!sbi->s_group_desc[group_desc]) {
-               ext4_error(sb, "ext4_get_group_desc",
-                          "Group descriptor not loaded - "
+               ext4_error(sb, "Group descriptor not loaded - "
                           "block_group = %u, group_desc = %u, desc = %u",
                           block_group, group_desc, offset);
                return NULL;
@@ -282,9 +275,7 @@ static int ext4_valid_block_bitmap(struct super_block *sb,
                return 1;
 
 err_out:
-       ext4_error(sb, __func__,
-                       "Invalid block bitmap - "
-                       "block_group = %d, block = %llu",
+       ext4_error(sb, "Invalid block bitmap - block_group = %d, block = %llu",
                        block_group, bitmap_blk);
        return 0;
 }
@@ -311,8 +302,7 @@ ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
        bitmap_blk = ext4_block_bitmap(sb, desc);
        bh = sb_getblk(sb, bitmap_blk);
        if (unlikely(!bh)) {
-               ext4_error(sb, __func__,
-                           "Cannot read block bitmap - "
+               ext4_error(sb, "Cannot read block bitmap - "
                            "block_group = %u, block_bitmap = %llu",
                            block_group, bitmap_blk);
                return NULL;
@@ -354,8 +344,7 @@ ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
        set_bitmap_uptodate(bh);
        if (bh_submit_read(bh) < 0) {
                put_bh(bh);
-               ext4_error(sb, __func__,
-                           "Cannot read block bitmap - "
+               ext4_error(sb, "Cannot read block bitmap - "
                            "block_group = %u, block_bitmap = %llu",
                            block_group, bitmap_blk);
                return NULL;
@@ -419,8 +408,7 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
            in_range(block, ext4_inode_table(sb, desc), sbi->s_itb_per_group) ||
            in_range(block + count - 1, ext4_inode_table(sb, desc),
                     sbi->s_itb_per_group)) {
-               ext4_error(sb, __func__,
-                          "Adding blocks in system zones - "
+               ext4_error(sb, "Adding blocks in system zones - "
                           "Block = %llu, count = %lu",
                           block, count);
                goto error_return;
@@ -453,8 +441,7 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
                BUFFER_TRACE(bitmap_bh, "clear bit");
                if (!ext4_clear_bit_atomic(ext4_group_lock_ptr(sb, block_group),
                                                bit + i, bitmap_bh->b_data)) {
-                       ext4_error(sb, __func__,
-                                  "bit already cleared for block %llu",
+                       ext4_error(sb, "bit already cleared for block %llu",
                                   (ext4_fsblk_t)(block + i));
                        BUFFER_TRACE(bitmap_bh, "bit already cleared");
                } else {
index a60ab9aad57d4f5807adb72b702042056132e08a..983f0e1274939b4506c91ea23c3c7d38ae624a88 100644 (file)
@@ -205,14 +205,14 @@ void ext4_release_system_zone(struct super_block *sb)
                entry = rb_entry(n, struct ext4_system_zone, node);
                kmem_cache_free(ext4_system_zone_cachep, entry);
                if (!parent)
-                       EXT4_SB(sb)->system_blks.rb_node = NULL;
+                       EXT4_SB(sb)->system_blks = RB_ROOT;
                else if (parent->rb_left == n)
                        parent->rb_left = NULL;
                else if (parent->rb_right == n)
                        parent->rb_right = NULL;
                n = parent;
        }
-       EXT4_SB(sb)->system_blks.rb_node = NULL;
+       EXT4_SB(sb)->system_blks = RB_ROOT;
 }
 
 /*
index 9dc93168e2623ae09d26b1c7287f4fc30975e077..86cb6d86a04806d260d0078efff6c45d4eb9d633 100644 (file)
@@ -83,10 +83,12 @@ int ext4_check_dir_entry(const char *function, struct inode *dir,
                error_msg = "inode out of bounds";
 
        if (error_msg != NULL)
-               ext4_error(dir->i_sb, function,
-                       "bad entry in directory #%lu: %s - "
-                       "offset=%u, inode=%u, rec_len=%d, name_len=%d",
-                       dir->i_ino, error_msg, offset,
+               __ext4_error(dir->i_sb, function,
+                       "bad entry in directory #%lu: %s - block=%llu"
+                       "offset=%u(%u), inode=%u, rec_len=%d, name_len=%d",
+                       dir->i_ino, error_msg, 
+                       (unsigned long long) bh->b_blocknr,     
+                       (unsigned) (offset%bh->b_size), offset,
                        le32_to_cpu(de->inode),
                        rlen, de->name_len);
        return error_msg == NULL ? 1 : 0;
@@ -150,7 +152,7 @@ static int ext4_readdir(struct file *filp,
                 */
                if (!bh) {
                        if (!dir_has_error) {
-                               ext4_error(sb, __func__, "directory #%lu "
+                               ext4_error(sb, "directory #%lu "
                                           "contains a hole at offset %Lu",
                                           inode->i_ino,
                                           (unsigned long long) filp->f_pos);
@@ -303,7 +305,7 @@ static void free_rb_tree_fname(struct rb_root *root)
                        kfree(old);
                }
                if (!parent)
-                       root->rb_node = NULL;
+                       *root = RB_ROOT;
                else if (parent->rb_left == n)
                        parent->rb_left = NULL;
                else if (parent->rb_right == n)
index 4cedc91ec59d79306dd307bd9f46a7be95931044..bf938cf7c5f0f273330dc02223e5ddc88dc28b86 100644 (file)
 #define ext4_debug(f, a...)    do {} while (0)
 #endif
 
+#define EXT4_ERROR_INODE(inode, fmt, a...) \
+       ext4_error_inode(__func__, (inode), (fmt), ## a);
+
+#define EXT4_ERROR_FILE(file, fmt, a...)       \
+       ext4_error_file(__func__, (file), (fmt), ## a);
+
 /* data type for block offset of block group */
 typedef int ext4_grpblk_t;
 
@@ -133,14 +139,14 @@ struct mpage_da_data {
        int pages_written;
        int retval;
 };
-#define        DIO_AIO_UNWRITTEN       0x1
+#define        EXT4_IO_UNWRITTEN       0x1
 typedef struct ext4_io_end {
        struct list_head        list;           /* per-file finished AIO list */
        struct inode            *inode;         /* file being written to */
        unsigned int            flag;           /* unwritten or not */
-       int                     error;          /* I/O error code */
-       ext4_lblk_t             offset;         /* offset in the file */
-       size_t                  size;           /* size of the extent */
+       struct page             *page;          /* page struct for buffer write */
+       loff_t                  offset;         /* offset in the file */
+       ssize_t                 size;           /* size of the extent */
        struct work_struct      work;           /* data work queue */
 } ext4_io_end_t;
 
@@ -284,10 +290,12 @@ struct flex_groups {
 #define EXT4_TOPDIR_FL                 0x00020000 /* Top of directory hierarchies*/
 #define EXT4_HUGE_FILE_FL               0x00040000 /* Set to each huge file */
 #define EXT4_EXTENTS_FL                        0x00080000 /* Inode uses extents */
+#define EXT4_EA_INODE_FL               0x00200000 /* Inode used for large EA */
+#define EXT4_EOFBLOCKS_FL              0x00400000 /* Blocks allocated beyond EOF */
 #define EXT4_RESERVED_FL               0x80000000 /* reserved for ext4 lib */
 
-#define EXT4_FL_USER_VISIBLE           0x000BDFFF /* User visible flags */
-#define EXT4_FL_USER_MODIFIABLE                0x000B80FF /* User modifiable flags */
+#define EXT4_FL_USER_VISIBLE           0x004BDFFF /* User visible flags */
+#define EXT4_FL_USER_MODIFIABLE                0x004B80FF /* User modifiable flags */
 
 /* Flags that should be inherited by new inodes from their parent. */
 #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
@@ -313,17 +321,6 @@ static inline __u32 ext4_mask_flags(umode_t mode, __u32 flags)
                return flags & EXT4_OTHER_FLMASK;
 }
 
-/*
- * Inode dynamic state flags
- */
-#define EXT4_STATE_JDATA               0x00000001 /* journaled data exists */
-#define EXT4_STATE_NEW                 0x00000002 /* inode is newly created */
-#define EXT4_STATE_XATTR               0x00000004 /* has in-inode xattrs */
-#define EXT4_STATE_NO_EXPAND           0x00000008 /* No space for expansion */
-#define EXT4_STATE_DA_ALLOC_CLOSE      0x00000010 /* Alloc DA blks on close */
-#define EXT4_STATE_EXT_MIGRATE         0x00000020 /* Inode is migrating */
-#define EXT4_STATE_DIO_UNWRITTEN       0x00000040 /* need convert on dio done*/
-
 /* Used to pass group descriptor data when online resize is done */
 struct ext4_new_group_input {
        __u32 group;            /* Group number for this data */
@@ -364,19 +361,20 @@ struct ext4_new_group_data {
        /* caller is from the direct IO path, request to creation of an
        unitialized extents if not allocated, split the uninitialized
        extent if blocks has been preallocated already*/
-#define EXT4_GET_BLOCKS_DIO                    0x0008
+#define EXT4_GET_BLOCKS_PRE_IO                 0x0008
 #define EXT4_GET_BLOCKS_CONVERT                        0x0010
-#define EXT4_GET_BLOCKS_DIO_CREATE_EXT         (EXT4_GET_BLOCKS_DIO|\
+#define EXT4_GET_BLOCKS_IO_CREATE_EXT          (EXT4_GET_BLOCKS_PRE_IO|\
+                                        EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
+       /* Convert extent to initialized after IO complete */
+#define EXT4_GET_BLOCKS_IO_CONVERT_EXT         (EXT4_GET_BLOCKS_CONVERT|\
                                         EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
-       /* Convert extent to initialized after direct IO complete */
-#define EXT4_GET_BLOCKS_DIO_CONVERT_EXT                (EXT4_GET_BLOCKS_CONVERT|\
-                                        EXT4_GET_BLOCKS_DIO_CREATE_EXT)
 
 /*
  * Flags used by ext4_free_blocks
  */
 #define EXT4_FREE_BLOCKS_METADATA      0x0001
 #define EXT4_FREE_BLOCKS_FORGET                0x0002
+#define EXT4_FREE_BLOCKS_VALIDATED     0x0004
 
 /*
  * ioctl commands
@@ -630,7 +628,7 @@ struct ext4_inode_info {
         * near to their parent directory's inode.
         */
        ext4_group_t    i_block_group;
-       __u32   i_state;                /* Dynamic state flags for ext4 */
+       unsigned long   i_state_flags;          /* Dynamic state flags */
 
        ext4_lblk_t             i_dir_start_lookup;
 #ifdef CONFIG_EXT4_FS_XATTR
@@ -708,8 +706,9 @@ struct ext4_inode_info {
        qsize_t i_reserved_quota;
 #endif
 
-       /* completed async DIOs that might need unwritten extents handling */
-       struct list_head i_aio_dio_complete_list;
+       /* completed IOs that might need unwritten extents handling */
+       struct list_head i_completed_io_list;
+       spinlock_t i_completed_io_lock;
        /* current io_end structure for async DIO write*/
        ext4_io_end_t *cur_aio_dio;
 
@@ -760,6 +759,7 @@ struct ext4_inode_info {
 #define EXT4_MOUNT_QUOTA               0x80000 /* Some quota option set */
 #define EXT4_MOUNT_USRQUOTA            0x100000 /* "old" user quota */
 #define EXT4_MOUNT_GRPQUOTA            0x200000 /* "old" group quota */
+#define EXT4_MOUNT_DIOREAD_NOLOCK      0x400000 /* Enable support for dio read nolocking */
 #define EXT4_MOUNT_JOURNAL_CHECKSUM    0x800000 /* Journal checksums */
 #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT        0x1000000 /* Journal Async Commit */
 #define EXT4_MOUNT_I_VERSION            0x2000000 /* i_version support */
@@ -1050,6 +1050,34 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
                (ino >= EXT4_FIRST_INO(sb) &&
                 ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
 }
+
+/*
+ * Inode dynamic state flags
+ */
+enum {
+       EXT4_STATE_JDATA,               /* journaled data exists */
+       EXT4_STATE_NEW,                 /* inode is newly created */
+       EXT4_STATE_XATTR,               /* has in-inode xattrs */
+       EXT4_STATE_NO_EXPAND,           /* No space for expansion */
+       EXT4_STATE_DA_ALLOC_CLOSE,      /* Alloc DA blks on close */
+       EXT4_STATE_EXT_MIGRATE,         /* Inode is migrating */
+       EXT4_STATE_DIO_UNWRITTEN,       /* need convert on dio done*/
+};
+
+static inline int ext4_test_inode_state(struct inode *inode, int bit)
+{
+       return test_bit(bit, &EXT4_I(inode)->i_state_flags);
+}
+
+static inline void ext4_set_inode_state(struct inode *inode, int bit)
+{
+       set_bit(bit, &EXT4_I(inode)->i_state_flags);
+}
+
+static inline void ext4_clear_inode_state(struct inode *inode, int bit)
+{
+       clear_bit(bit, &EXT4_I(inode)->i_state_flags);
+}
 #else
 /* Assume that user mode programs are passing in an ext4fs superblock, not
  * a kernel struct super_block.  This will allow us to call the feature-test
@@ -1126,6 +1154,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
 #define EXT4_FEATURE_INCOMPAT_64BIT            0x0080
 #define EXT4_FEATURE_INCOMPAT_MMP               0x0100
 #define EXT4_FEATURE_INCOMPAT_FLEX_BG          0x0200
+#define EXT4_FEATURE_INCOMPAT_EA_INODE         0x0400 /* EA in inode */
+#define EXT4_FEATURE_INCOMPAT_DIRDATA          0x1000 /* data in dirent */
 
 #define EXT4_FEATURE_COMPAT_SUPP       EXT2_FEATURE_COMPAT_EXT_ATTR
 #define EXT4_FEATURE_INCOMPAT_SUPP     (EXT4_FEATURE_INCOMPAT_FILETYPE| \
@@ -1416,7 +1446,7 @@ int ext4_get_block(struct inode *inode, sector_t iblock,
                                struct buffer_head *bh_result, int create);
 
 extern struct inode *ext4_iget(struct super_block *, unsigned long);
-extern int  ext4_write_inode(struct inode *, int);
+extern int  ext4_write_inode(struct inode *, struct writeback_control *);
 extern int  ext4_setattr(struct dentry *, struct iattr *);
 extern int  ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
                                struct kstat *stat);
@@ -1439,7 +1469,7 @@ extern int ext4_block_truncate_page(handle_t *handle,
                struct address_space *mapping, loff_t from);
 extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 extern qsize_t *ext4_get_reserved_space(struct inode *inode);
-extern int flush_aio_dio_completed_IO(struct inode *inode);
+extern int flush_completed_IO(struct inode *inode);
 extern void ext4_da_update_reserve_space(struct inode *inode,
                                        int used, int quota_claim);
 /* ioctl.c */
@@ -1465,13 +1495,20 @@ extern int ext4_group_extend(struct super_block *sb,
                                ext4_fsblk_t n_blocks_count);
 
 /* super.c */
-extern void ext4_error(struct super_block *, const char *, const char *, ...)
+extern void __ext4_error(struct super_block *, const char *, const char *, ...)
+       __attribute__ ((format (printf, 3, 4)));
+#define ext4_error(sb, message...)     __ext4_error(sb, __func__, ## message)
+extern void ext4_error_inode(const char *, struct inode *, const char *, ...)
+       __attribute__ ((format (printf, 3, 4)));
+extern void ext4_error_file(const char *, struct file *, const char *, ...)
        __attribute__ ((format (printf, 3, 4)));
 extern void __ext4_std_error(struct super_block *, const char *, int);
 extern void ext4_abort(struct super_block *, const char *, const char *, ...)
        __attribute__ ((format (printf, 3, 4)));
-extern void ext4_warning(struct super_block *, const char *, const char *, ...)
+extern void __ext4_warning(struct super_block *, const char *,
+                         const char *, ...)
        __attribute__ ((format (printf, 3, 4)));
+#define ext4_warning(sb, message...)   __ext4_warning(sb, __func__, ## message)
 extern void ext4_msg(struct super_block *, const char *, const char *, ...)
        __attribute__ ((format (printf, 3, 4)));
 extern void ext4_grp_locked_error(struct super_block *, ext4_group_t,
@@ -1744,7 +1781,7 @@ extern void ext4_ext_release(struct super_block *);
 extern long ext4_fallocate(struct inode *inode, int mode, loff_t offset,
                          loff_t len);
 extern int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
-                         loff_t len);
+                         ssize_t len);
 extern int ext4_get_blocks(handle_t *handle, struct inode *inode,
                           sector_t block, unsigned int max_blocks,
                           struct buffer_head *bh, int flags);
@@ -1756,6 +1793,15 @@ extern int ext4_move_extents(struct file *o_filp, struct file *d_filp,
                             __u64 len, __u64 *moved_len);
 
 
+/* BH_Uninit flag: blocks are allocated but uninitialized on disk */
+enum ext4_state_bits {
+       BH_Uninit       /* blocks are allocated but uninitialized on disk */
+         = BH_JBDPrivateStart,
+};
+
+BUFFER_FNS(Uninit, uninit)
+TAS_BUFFER_FNS(Uninit, uninit)
+
 /*
  * Add new method to test wether block and inode bitmaps are properly
  * initialized. With uninit_bg reading the block from disk is not enough
@@ -1773,6 +1819,8 @@ static inline void set_bitmap_uptodate(struct buffer_head *bh)
        set_bit(BH_BITMAP_UPTODATE, &(bh)->b_state);
 }
 
+#define in_range(b, first, len)        ((b) >= (first) && (b) <= (first) + (len) - 1)
+
 #endif /* __KERNEL__ */
 
 #endif /* _EXT4_H */
index b57e5c711b6de1048b98afb238fbdca874134497..53d2764d71caee518c5a3639c65d5708456ed25c 100644 (file)
@@ -125,14 +125,14 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
                        ext4_journal_abort_handle(where, __func__, bh,
                                                  handle, err);
        } else {
-               if (inode && bh)
+               if (inode)
                        mark_buffer_dirty_inode(bh, inode);
                else
                        mark_buffer_dirty(bh);
                if (inode && inode_needs_sync(inode)) {
                        sync_dirty_buffer(bh);
                        if (buffer_req(bh) && !buffer_uptodate(bh)) {
-                               ext4_error(inode->i_sb, __func__,
+                               ext4_error(inode->i_sb,
                                           "IO error syncing inode, "
                                           "inode=%lu, block=%llu",
                                           inode->i_ino,
index 05eca817d7046258f54d3a8715dafb946ff5e155..b79ad5126468867efb31cab9835517e38e3bce04 100644 (file)
@@ -304,4 +304,28 @@ static inline int ext4_should_writeback_data(struct inode *inode)
        return 0;
 }
 
+/*
+ * This function controls whether or not we should try to go down the
+ * dioread_nolock code paths, which makes it safe to avoid taking
+ * i_mutex for direct I/O reads.  This only works for extent-based
+ * files, and it doesn't work for nobh or if data journaling is
+ * enabled, since the dioread_nolock code uses b_private to pass
+ * information back to the I/O completion handler, and this conflicts
+ * with the jbd's use of b_private.
+ */
+static inline int ext4_should_dioread_nolock(struct inode *inode)
+{
+       if (!test_opt(inode->i_sb, DIOREAD_NOLOCK))
+               return 0;
+       if (test_opt(inode->i_sb, NOBH))
+               return 0;
+       if (!S_ISREG(inode->i_mode))
+               return 0;
+       if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
+               return 0;
+       if (ext4_should_journal_data(inode))
+               return 0;
+       return 1;
+}
+
 #endif /* _EXT4_JBD2_H */
index 765a4826b118b949a448097d614698d4c7bc4621..94c8ee81f5e1e38aa52964f95ab6b7dc9a51eae4 100644 (file)
@@ -195,8 +195,7 @@ static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode,
                if (S_ISREG(inode->i_mode))
                        block_group++;
        }
-       bg_start = (block_group * EXT4_BLOCKS_PER_GROUP(inode->i_sb)) +
-               le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_first_data_block);
+       bg_start = ext4_group_first_block_no(inode->i_sb, block_group);
        last_block = ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es) - 1;
 
        /*
@@ -440,7 +439,7 @@ static int __ext4_ext_check(const char *function, struct inode *inode,
        return 0;
 
 corrupted:
-       ext4_error(inode->i_sb, function,
+       __ext4_error(inode->i_sb, function,
                        "bad header/extent in inode #%lu: %s - magic %x, "
                        "entries %u, max %u(%u), depth %u(%u)",
                        inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic),
@@ -703,7 +702,12 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
                }
                eh = ext_block_hdr(bh);
                ppos++;
-               BUG_ON(ppos > depth);
+               if (unlikely(ppos > depth)) {
+                       put_bh(bh);
+                       EXT4_ERROR_INODE(inode,
+                                        "ppos %d > depth %d", ppos, depth);
+                       goto err;
+               }
                path[ppos].p_bh = bh;
                path[ppos].p_hdr = eh;
                i--;
@@ -749,7 +753,12 @@ int ext4_ext_insert_index(handle_t *handle, struct inode *inode,
        if (err)
                return err;
 
-       BUG_ON(logical == le32_to_cpu(curp->p_idx->ei_block));
+       if (unlikely(logical == le32_to_cpu(curp->p_idx->ei_block))) {
+               EXT4_ERROR_INODE(inode,
+                                "logical %d == ei_block %d!",
+                                logical, le32_to_cpu(curp->p_idx->ei_block));
+               return -EIO;
+       }
        len = EXT_MAX_INDEX(curp->p_hdr) - curp->p_idx;
        if (logical > le32_to_cpu(curp->p_idx->ei_block)) {
                /* insert after */
@@ -779,9 +788,17 @@ int ext4_ext_insert_index(handle_t *handle, struct inode *inode,
        ext4_idx_store_pblock(ix, ptr);
        le16_add_cpu(&curp->p_hdr->eh_entries, 1);
 
-       BUG_ON(le16_to_cpu(curp->p_hdr->eh_entries)
-                            > le16_to_cpu(curp->p_hdr->eh_max));
-       BUG_ON(ix > EXT_LAST_INDEX(curp->p_hdr));
+       if (unlikely(le16_to_cpu(curp->p_hdr->eh_entries)
+                            > le16_to_cpu(curp->p_hdr->eh_max))) {
+               EXT4_ERROR_INODE(inode,
+                                "logical %d == ei_block %d!",
+                                logical, le32_to_cpu(curp->p_idx->ei_block));
+               return -EIO;
+       }
+       if (unlikely(ix > EXT_LAST_INDEX(curp->p_hdr))) {
+               EXT4_ERROR_INODE(inode, "ix > EXT_LAST_INDEX!");
+               return -EIO;
+       }
 
        err = ext4_ext_dirty(handle, inode, curp);
        ext4_std_error(inode->i_sb, err);
@@ -819,7 +836,10 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
 
        /* if current leaf will be split, then we should use
         * border from split point */
-       BUG_ON(path[depth].p_ext > EXT_MAX_EXTENT(path[depth].p_hdr));
+       if (unlikely(path[depth].p_ext > EXT_MAX_EXTENT(path[depth].p_hdr))) {
+               EXT4_ERROR_INODE(inode, "p_ext > EXT_MAX_EXTENT!");
+               return -EIO;
+       }
        if (path[depth].p_ext != EXT_MAX_EXTENT(path[depth].p_hdr)) {
                border = path[depth].p_ext[1].ee_block;
                ext_debug("leaf will be split."
@@ -860,7 +880,11 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
 
        /* initialize new leaf */
        newblock = ablocks[--a];
-       BUG_ON(newblock == 0);
+       if (unlikely(newblock == 0)) {
+               EXT4_ERROR_INODE(inode, "newblock == 0!");
+               err = -EIO;
+               goto cleanup;
+       }
        bh = sb_getblk(inode->i_sb, newblock);
        if (!bh) {
                err = -EIO;
@@ -880,7 +904,14 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
        ex = EXT_FIRST_EXTENT(neh);
 
        /* move remainder of path[depth] to the new leaf */
-       BUG_ON(path[depth].p_hdr->eh_entries != path[depth].p_hdr->eh_max);
+       if (unlikely(path[depth].p_hdr->eh_entries !=
+                    path[depth].p_hdr->eh_max)) {
+               EXT4_ERROR_INODE(inode, "eh_entries %d != eh_max %d!",
+                                path[depth].p_hdr->eh_entries,
+                                path[depth].p_hdr->eh_max);
+               err = -EIO;
+               goto cleanup;
+       }
        /* start copy from next extent */
        /* TODO: we could do it by single memmove */
        m = 0;
@@ -927,7 +958,11 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
 
        /* create intermediate indexes */
        k = depth - at - 1;
-       BUG_ON(k < 0);
+       if (unlikely(k < 0)) {
+               EXT4_ERROR_INODE(inode, "k %d < 0!", k);
+               err = -EIO;
+               goto cleanup;
+       }
        if (k)
                ext_debug("create %d intermediate indices\n", k);
        /* insert new index into current index block */
@@ -964,8 +999,14 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
 
                ext_debug("cur 0x%p, last 0x%p\n", path[i].p_idx,
                                EXT_MAX_INDEX(path[i].p_hdr));
-               BUG_ON(EXT_MAX_INDEX(path[i].p_hdr) !=
-                               EXT_LAST_INDEX(path[i].p_hdr));
+               if (unlikely(EXT_MAX_INDEX(path[i].p_hdr) !=
+                                       EXT_LAST_INDEX(path[i].p_hdr))) {
+                       EXT4_ERROR_INODE(inode,
+                                        "EXT_MAX_INDEX != EXT_LAST_INDEX ee_block %d!",
+                                        le32_to_cpu(path[i].p_ext->ee_block));
+                       err = -EIO;
+                       goto cleanup;
+               }
                while (path[i].p_idx <= EXT_MAX_INDEX(path[i].p_hdr)) {
                        ext_debug("%d: move %d:%llu in new index %llu\n", i,
                                        le32_to_cpu(path[i].p_idx->ei_block),
@@ -1203,7 +1244,10 @@ ext4_ext_search_left(struct inode *inode, struct ext4_ext_path *path,
        struct ext4_extent *ex;
        int depth, ee_len;
 
-       BUG_ON(path == NULL);
+       if (unlikely(path == NULL)) {
+               EXT4_ERROR_INODE(inode, "path == NULL *logical %d!", *logical);
+               return -EIO;
+       }
        depth = path->p_depth;
        *phys = 0;
 
@@ -1217,15 +1261,33 @@ ext4_ext_search_left(struct inode *inode, struct ext4_ext_path *path,
        ex = path[depth].p_ext;
        ee_len = ext4_ext_get_actual_len(ex);
        if (*logical < le32_to_cpu(ex->ee_block)) {
-               BUG_ON(EXT_FIRST_EXTENT(path[depth].p_hdr) != ex);
+               if (unlikely(EXT_FIRST_EXTENT(path[depth].p_hdr) != ex)) {
+                       EXT4_ERROR_INODE(inode,
+                                        "EXT_FIRST_EXTENT != ex *logical %d ee_block %d!",
+                                        *logical, le32_to_cpu(ex->ee_block));
+                       return -EIO;
+               }
                while (--depth >= 0) {
                        ix = path[depth].p_idx;
-                       BUG_ON(ix != EXT_FIRST_INDEX(path[depth].p_hdr));
+                       if (unlikely(ix != EXT_FIRST_INDEX(path[depth].p_hdr))) {
+                               EXT4_ERROR_INODE(inode,
+                                 "ix (%d) != EXT_FIRST_INDEX (%d) (depth %d)!",
+                                 ix != NULL ? ix->ei_block : 0,
+                                 EXT_FIRST_INDEX(path[depth].p_hdr) != NULL ?
+                                   EXT_FIRST_INDEX(path[depth].p_hdr)->ei_block : 0,
+                                 depth);
+                               return -EIO;
+                       }
                }
                return 0;
        }
 
-       BUG_ON(*logical < (le32_to_cpu(ex->ee_block) + ee_len));
+       if (unlikely(*logical < (le32_to_cpu(ex->ee_block) + ee_len))) {
+               EXT4_ERROR_INODE(inode,
+                                "logical %d < ee_block %d + ee_len %d!",
+                                *logical, le32_to_cpu(ex->ee_block), ee_len);
+               return -EIO;
+       }
 
        *logical = le32_to_cpu(ex->ee_block) + ee_len - 1;
        *phys = ext_pblock(ex) + ee_len - 1;
@@ -1251,7 +1313,10 @@ ext4_ext_search_right(struct inode *inode, struct ext4_ext_path *path,
        int depth;      /* Note, NOT eh_depth; depth from top of tree */
        int ee_len;
 
-       BUG_ON(path == NULL);
+       if (unlikely(path == NULL)) {
+               EXT4_ERROR_INODE(inode, "path == NULL *logical %d!", *logical);
+               return -EIO;
+       }
        depth = path->p_depth;
        *phys = 0;
 
@@ -1265,17 +1330,32 @@ ext4_ext_search_right(struct inode *inode, struct ext4_ext_path *path,
        ex = path[depth].p_ext;
        ee_len = ext4_ext_get_actual_len(ex);
        if (*logical < le32_to_cpu(ex->ee_block)) {
-               BUG_ON(EXT_FIRST_EXTENT(path[depth].p_hdr) != ex);
+               if (unlikely(EXT_FIRST_EXTENT(path[depth].p_hdr) != ex)) {
+                       EXT4_ERROR_INODE(inode,
+                                        "first_extent(path[%d].p_hdr) != ex",
+                                        depth);
+                       return -EIO;
+               }
                while (--depth >= 0) {
                        ix = path[depth].p_idx;
-                       BUG_ON(ix != EXT_FIRST_INDEX(path[depth].p_hdr));
+                       if (unlikely(ix != EXT_FIRST_INDEX(path[depth].p_hdr))) {
+                               EXT4_ERROR_INODE(inode,
+                                                "ix != EXT_FIRST_INDEX *logical %d!",
+                                                *logical);
+                               return -EIO;
+                       }
                }
                *logical = le32_to_cpu(ex->ee_block);
                *phys = ext_pblock(ex);
                return 0;
        }
 
-       BUG_ON(*logical < (le32_to_cpu(ex->ee_block) + ee_len));
+       if (unlikely(*logical < (le32_to_cpu(ex->ee_block) + ee_len))) {
+               EXT4_ERROR_INODE(inode,
+                                "logical %d < ee_block %d + ee_len %d!",
+                                *logical, le32_to_cpu(ex->ee_block), ee_len);
+               return -EIO;
+       }
 
        if (ex != EXT_LAST_EXTENT(path[depth].p_hdr)) {
                /* next allocated block in this leaf */
@@ -1414,8 +1494,12 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
 
        eh = path[depth].p_hdr;
        ex = path[depth].p_ext;
-       BUG_ON(ex == NULL);
-       BUG_ON(eh == NULL);
+
+       if (unlikely(ex == NULL || eh == NULL)) {
+               EXT4_ERROR_INODE(inode,
+                                "ex %p == NULL or eh %p == NULL", ex, eh);
+               return -EIO;
+       }
 
        if (depth == 0) {
                /* there is no tree at all */
@@ -1538,8 +1622,9 @@ int ext4_ext_try_to_merge(struct inode *inode,
                merge_done = 1;
                WARN_ON(eh->eh_entries == 0);
                if (!eh->eh_entries)
-                       ext4_error(inode->i_sb, "ext4_ext_try_to_merge",
-                          "inode#%lu, eh->eh_entries = 0!", inode->i_ino);
+                       ext4_error(inode->i_sb,
+                                  "inode#%lu, eh->eh_entries = 0!",
+                                  inode->i_ino);
        }
 
        return merge_done;
@@ -1612,13 +1697,19 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
        ext4_lblk_t next;
        unsigned uninitialized = 0;
 
-       BUG_ON(ext4_ext_get_actual_len(newext) == 0);
+       if (unlikely(ext4_ext_get_actual_len(newext) == 0)) {
+               EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0");
+               return -EIO;
+       }
        depth = ext_depth(inode);
        ex = path[depth].p_ext;
-       BUG_ON(path[depth].p_hdr == NULL);
+       if (unlikely(path[depth].p_hdr == NULL)) {
+               EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
+               return -EIO;
+       }
 
        /* try to insert block into found extent and return */
-       if (ex && (flag != EXT4_GET_BLOCKS_DIO_CREATE_EXT)
+       if (ex && !(flag & EXT4_GET_BLOCKS_PRE_IO)
                && ext4_can_extents_be_merged(inode, ex, newext)) {
                ext_debug("append [%d]%d block to %d:[%d]%d (from %llu)\n",
                                ext4_ext_is_uninitialized(newext),
@@ -1739,7 +1830,7 @@ has_space:
 
 merge:
        /* try to merge extents to the right */
-       if (flag != EXT4_GET_BLOCKS_DIO_CREATE_EXT)
+       if (!(flag & EXT4_GET_BLOCKS_PRE_IO))
                ext4_ext_try_to_merge(inode, path, nearex);
 
        /* try to merge extents to the left */
@@ -1787,7 +1878,11 @@ int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
                }
 
                depth = ext_depth(inode);
-               BUG_ON(path[depth].p_hdr == NULL);
+               if (unlikely(path[depth].p_hdr == NULL)) {
+                       EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
+                       err = -EIO;
+                       break;
+               }
                ex = path[depth].p_ext;
                next = ext4_ext_next_allocated_block(path);
 
@@ -1838,7 +1933,11 @@ int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
                        cbex.ec_type = EXT4_EXT_CACHE_EXTENT;
                }
 
-               BUG_ON(cbex.ec_len == 0);
+               if (unlikely(cbex.ec_len == 0)) {
+                       EXT4_ERROR_INODE(inode, "cbex.ec_len == 0");
+                       err = -EIO;
+                       break;
+               }
                err = func(inode, path, &cbex, ex, cbdata);
                ext4_ext_drop_refs(path);
 
@@ -1952,7 +2051,7 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
 
        BUG_ON(cex->ec_type != EXT4_EXT_CACHE_GAP &&
                        cex->ec_type != EXT4_EXT_CACHE_EXTENT);
-       if (block >= cex->ec_block && block < cex->ec_block + cex->ec_len) {
+       if (in_range(block, cex->ec_block, cex->ec_len)) {
                ex->ee_block = cpu_to_le32(cex->ec_block);
                ext4_ext_store_pblock(ex, cex->ec_start);
                ex->ee_len = cpu_to_le16(cex->ec_len);
@@ -1981,7 +2080,10 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
        /* free index block */
        path--;
        leaf = idx_pblock(path->p_idx);
-       BUG_ON(path->p_hdr->eh_entries == 0);
+       if (unlikely(path->p_hdr->eh_entries == 0)) {
+               EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
+               return -EIO;
+       }
        err = ext4_ext_get_access(handle, inode, path);
        if (err)
                return err;
@@ -2119,8 +2221,10 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
        if (!path[depth].p_hdr)
                path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
        eh = path[depth].p_hdr;
-       BUG_ON(eh == NULL);
-
+       if (unlikely(path[depth].p_hdr == NULL)) {
+               EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
+               return -EIO;
+       }
        /* find where to start removing */
        ex = EXT_LAST_EXTENT(eh);
 
@@ -2983,7 +3087,7 @@ fix_extent_len:
        ext4_ext_dirty(handle, inode, path + depth);
        return err;
 }
-static int ext4_convert_unwritten_extents_dio(handle_t *handle,
+static int ext4_convert_unwritten_extents_endio(handle_t *handle,
                                              struct inode *inode,
                                              struct ext4_ext_path *path)
 {
@@ -3063,8 +3167,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
                  flags, allocated);
        ext4_ext_show_leaf(inode, path);
 
-       /* DIO get_block() before submit the IO, split the extent */
-       if (flags == EXT4_GET_BLOCKS_DIO_CREATE_EXT) {
+       /* get_block() before submit the IO, split the extent */
+       if ((flags & EXT4_GET_BLOCKS_PRE_IO)) {
                ret = ext4_split_unwritten_extents(handle,
                                                inode, path, iblock,
                                                max_blocks, flags);
@@ -3074,14 +3178,16 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
                 * completed
                 */
                if (io)
-                       io->flag = DIO_AIO_UNWRITTEN;
+                       io->flag = EXT4_IO_UNWRITTEN;
                else
-                       EXT4_I(inode)->i_state |= EXT4_STATE_DIO_UNWRITTEN;
+                       ext4_set_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN);
+               if (ext4_should_dioread_nolock(inode))
+                       set_buffer_uninit(bh_result);
                goto out;
        }
-       /* async DIO end_io complete, convert the filled extent to written */
-       if (flags == EXT4_GET_BLOCKS_DIO_CONVERT_EXT) {
-               ret = ext4_convert_unwritten_extents_dio(handle, inode,
+       /* IO end_io complete, convert the filled extent to written */
+       if ((flags & EXT4_GET_BLOCKS_CONVERT)) {
+               ret = ext4_convert_unwritten_extents_endio(handle, inode,
                                                        path);
                if (ret >= 0)
                        ext4_update_inode_fsync_trans(handle, inode, 1);
@@ -3185,7 +3291,7 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
 {
        struct ext4_ext_path *path = NULL;
        struct ext4_extent_header *eh;
-       struct ext4_extent newex, *ex;
+       struct ext4_extent newex, *ex, *last_ex;
        ext4_fsblk_t newblock;
        int err = 0, depth, ret, cache_type;
        unsigned int allocated = 0;
@@ -3237,10 +3343,10 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
         * this situation is possible, though, _during_ tree modification;
         * this is why assert can't be put in ext4_ext_find_extent()
         */
-       if (path[depth].p_ext == NULL && depth != 0) {
-               ext4_error(inode->i_sb, __func__, "bad extent address "
-                          "inode: %lu, iblock: %d, depth: %d",
-                          inode->i_ino, iblock, depth);
+       if (unlikely(path[depth].p_ext == NULL && depth != 0)) {
+               EXT4_ERROR_INODE(inode, "bad extent address "
+                                "iblock: %d, depth: %d pblock %lld",
+                                iblock, depth, path[depth].p_block);
                err = -EIO;
                goto out2;
        }
@@ -3258,7 +3364,7 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
                 */
                ee_len = ext4_ext_get_actual_len(ex);
                /* if found extent covers block, simply return it */
-               if (iblock >= ee_block && iblock < ee_block + ee_len) {
+               if (in_range(iblock, ee_block, ee_len)) {
                        newblock = iblock - ee_block + ee_start;
                        /* number of remaining blocks in the extent */
                        allocated = ee_len - (iblock - ee_block);
@@ -3350,21 +3456,35 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
        if (flags & EXT4_GET_BLOCKS_UNINIT_EXT){
                ext4_ext_mark_uninitialized(&newex);
                /*
-                * io_end structure was created for every async
-                * direct IO write to the middle of the file.
-                * To avoid unecessary convertion for every aio dio rewrite
-                * to the mid of file, here we flag the IO that is really
-                * need the convertion.
+                * io_end structure was created for every IO write to an
+                * uninitialized extent. To avoid unecessary conversion,
+                * here we flag the IO that really needs the conversion.
                 * For non asycn direct IO case, flag the inode state
                 * that we need to perform convertion when IO is done.
                 */
-               if (flags == EXT4_GET_BLOCKS_DIO_CREATE_EXT) {
+               if ((flags & EXT4_GET_BLOCKS_PRE_IO)) {
                        if (io)
-                               io->flag = DIO_AIO_UNWRITTEN;
+                               io->flag = EXT4_IO_UNWRITTEN;
                        else
-                               EXT4_I(inode)->i_state |=
-                                       EXT4_STATE_DIO_UNWRITTEN;;
+                               ext4_set_inode_state(inode,
+                                                    EXT4_STATE_DIO_UNWRITTEN);
+               }
+               if (ext4_should_dioread_nolock(inode))
+                       set_buffer_uninit(bh_result);
+       }
+
+       if (unlikely(EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL)) {
+               if (unlikely(!eh->eh_entries)) {
+                       EXT4_ERROR_INODE(inode,
+                                        "eh->eh_entries == 0 ee_block %d",
+                                        ex->ee_block);
+                       err = -EIO;
+                       goto out2;
                }
+               last_ex = EXT_LAST_EXTENT(eh);
+               if (iblock + ar.len > le32_to_cpu(last_ex->ee_block)
+                   + ext4_ext_get_actual_len(last_ex))
+                       EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL;
        }
        err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
        if (err) {
@@ -3499,6 +3619,13 @@ static void ext4_falloc_update_inode(struct inode *inode,
                        i_size_write(inode, new_size);
                if (new_size > EXT4_I(inode)->i_disksize)
                        ext4_update_i_disksize(inode, new_size);
+       } else {
+               /*
+                * Mark that we allocate beyond EOF so the subsequent truncate
+                * can proceed even if the new size is the same as i_size.
+                */
+               if (new_size > i_size_read(inode))
+                       EXT4_I(inode)->i_flags |= EXT4_EOFBLOCKS_FL;
        }
 
 }
@@ -3603,7 +3730,7 @@ retry:
  * Returns 0 on success.
  */
 int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
-                                   loff_t len)
+                                   ssize_t len)
 {
        handle_t *handle;
        ext4_lblk_t block;
@@ -3635,7 +3762,7 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
                map_bh.b_state = 0;
                ret = ext4_get_blocks(handle, inode, block,
                                      max_blocks, &map_bh,
-                                     EXT4_GET_BLOCKS_DIO_CONVERT_EXT);
+                                     EXT4_GET_BLOCKS_IO_CONVERT_EXT);
                if (ret <= 0) {
                        WARN_ON(ret <= 0);
                        printk(KERN_ERR "%s: ext4_ext_get_blocks "
@@ -3739,7 +3866,7 @@ static int ext4_xattr_fiemap(struct inode *inode,
        int error = 0;
 
        /* in-inode? */
-       if (EXT4_I(inode)->i_state & EXT4_STATE_XATTR) {
+       if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
                struct ext4_iloc iloc;
                int offset;     /* offset of xattr in inode */
 
@@ -3767,7 +3894,6 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                __u64 start, __u64 len)
 {
        ext4_lblk_t start_blk;
-       ext4_lblk_t len_blks;
        int error = 0;
 
        /* fallback to generic here if not in extents fmt */
@@ -3781,8 +3907,14 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
        if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
                error = ext4_xattr_fiemap(inode, fieinfo);
        } else {
+               ext4_lblk_t len_blks;
+               __u64 last_blk;
+
                start_blk = start >> inode->i_sb->s_blocksize_bits;
-               len_blks = len >> inode->i_sb->s_blocksize_bits;
+               last_blk = (start + len - 1) >> inode->i_sb->s_blocksize_bits;
+               if (last_blk >= EXT_MAX_BLOCK)
+                       last_blk = EXT_MAX_BLOCK-1;
+               len_blks = ((ext4_lblk_t) last_blk) - start_blk + 1;
 
                /*
                 * Walk the extent tree gathering extent information.
index 9630583cef280d541e87505eba2cfa37589266e3..d0776e410f340c8f60ad822321c6396436de4c98 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/jbd2.h>
 #include <linux/mount.h>
 #include <linux/path.h>
+#include <linux/quotaops.h>
 #include "ext4.h"
 #include "ext4_jbd2.h"
 #include "xattr.h"
@@ -35,9 +36,9 @@
  */
 static int ext4_release_file(struct inode *inode, struct file *filp)
 {
-       if (EXT4_I(inode)->i_state & EXT4_STATE_DA_ALLOC_CLOSE) {
+       if (ext4_test_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE)) {
                ext4_alloc_da_blocks(inode);
-               EXT4_I(inode)->i_state &= ~EXT4_STATE_DA_ALLOC_CLOSE;
+               ext4_clear_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
        }
        /* if we are the last writer on the inode, drop the block reservation */
        if ((filp->f_mode & FMODE_WRITE) &&
@@ -116,18 +117,16 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
                 * devices or filesystem images.
                 */
                memset(buf, 0, sizeof(buf));
-               path.mnt = mnt->mnt_parent;
-               path.dentry = mnt->mnt_mountpoint;
-               path_get(&path);
+               path.mnt = mnt;
+               path.dentry = mnt->mnt_root;
                cp = d_path(&path, buf, sizeof(buf));
-               path_put(&path);
                if (!IS_ERR(cp)) {
                        memcpy(sbi->s_es->s_last_mounted, cp,
                               sizeof(sbi->s_es->s_last_mounted));
                        sb->s_dirt = 1;
                }
        }
-       return generic_file_open(inode, filp);
+       return dquot_file_open(inode, filp);
 }
 
 const struct file_operations ext4_file_operations = {
index 98bd140aad01148b07a85d2ca7ee59b97c1ac8c4..0d0c3239c1cddaf0029aaf999dc14dbb65e44353 100644 (file)
@@ -63,7 +63,7 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
        if (inode->i_sb->s_flags & MS_RDONLY)
                return 0;
 
-       ret = flush_aio_dio_completed_IO(inode);
+       ret = flush_completed_IO(inode);
        if (ret < 0)
                return ret;
        
index f3624ead4f6c5136189e759d0bba8d0c9c2ca356..361c0b9962a8142b38823e8e9202d0d070c0130b 100644 (file)
@@ -76,8 +76,7 @@ unsigned ext4_init_inode_bitmap(struct super_block *sb, struct buffer_head *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)) {
-               ext4_error(sb, __func__, "Checksum bad for group %u",
-                          block_group);
+               ext4_error(sb, "Checksum bad for group %u", block_group);
                ext4_free_blks_set(sb, gdp, 0);
                ext4_free_inodes_set(sb, gdp, 0);
                ext4_itable_unused_set(sb, gdp, 0);
@@ -111,8 +110,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
        bitmap_blk = ext4_inode_bitmap(sb, desc);
        bh = sb_getblk(sb, bitmap_blk);
        if (unlikely(!bh)) {
-               ext4_error(sb, __func__,
-                           "Cannot read inode bitmap - "
+               ext4_error(sb, "Cannot read inode bitmap - "
                            "block_group = %u, inode_bitmap = %llu",
                            block_group, bitmap_blk);
                return NULL;
@@ -153,8 +151,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
        set_bitmap_uptodate(bh);
        if (bh_submit_read(bh) < 0) {
                put_bh(bh);
-               ext4_error(sb, __func__,
-                           "Cannot read inode bitmap - "
+               ext4_error(sb, "Cannot read inode bitmap - "
                            "block_group = %u, inode_bitmap = %llu",
                            block_group, bitmap_blk);
                return NULL;
@@ -217,10 +214,10 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
         * Note: we must free any quota before locking the superblock,
         * as writing the quota to disk may need the lock as well.
         */
-       vfs_dq_init(inode);
+       dquot_initialize(inode);
        ext4_xattr_delete_inode(handle, inode);
-       vfs_dq_free_inode(inode);
-       vfs_dq_drop(inode);
+       dquot_free_inode(inode);
+       dquot_drop(inode);
 
        is_directory = S_ISDIR(inode->i_mode);
 
@@ -229,8 +226,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
 
        es = EXT4_SB(sb)->s_es;
        if (ino < EXT4_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
-               ext4_error(sb, "ext4_free_inode",
-                          "reserved or nonexistent inode %lu", ino);
+               ext4_error(sb, "reserved or nonexistent inode %lu", ino);
                goto error_return;
        }
        block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
@@ -248,8 +244,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
        cleared = ext4_clear_bit_atomic(ext4_group_lock_ptr(sb, block_group),
                                        bit, bitmap_bh->b_data);
        if (!cleared)
-               ext4_error(sb, "ext4_free_inode",
-                          "bit already cleared for inode %lu", ino);
+               ext4_error(sb, "bit already cleared for inode %lu", ino);
        else {
                gdp = ext4_get_group_desc(sb, block_group, &bh2);
 
@@ -736,8 +731,7 @@ static int ext4_claim_inode(struct super_block *sb,
        if ((group == 0 && ino < EXT4_FIRST_INO(sb)) ||
                        ino > EXT4_INODES_PER_GROUP(sb)) {
                ext4_unlock_group(sb, group);
-               ext4_error(sb, __func__,
-                          "reserved inode or inode > inodes count - "
+               ext4_error(sb, "reserved inode or inode > inodes count - "
                           "block_group = %u, inode=%lu", group,
                           ino + group * EXT4_INODES_PER_GROUP(sb));
                return 1;
@@ -904,7 +898,7 @@ repeat_in_this_group:
                                BUFFER_TRACE(inode_bitmap_bh,
                                        "call ext4_handle_dirty_metadata");
                                err = ext4_handle_dirty_metadata(handle,
-                                                                inode,
+                                                                NULL,
                                                        inode_bitmap_bh);
                                if (err)
                                        goto fail;
@@ -1029,15 +1023,16 @@ got:
        inode->i_generation = sbi->s_next_generation++;
        spin_unlock(&sbi->s_next_gen_lock);
 
-       ei->i_state = EXT4_STATE_NEW;
+       ei->i_state_flags = 0;
+       ext4_set_inode_state(inode, EXT4_STATE_NEW);
 
        ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
 
        ret = inode;
-       if (vfs_dq_alloc_inode(inode)) {
-               err = -EDQUOT;
+       dquot_initialize(inode);
+       err = dquot_alloc_inode(inode);
+       if (err)
                goto fail_drop;
-       }
 
        err = ext4_init_acl(handle, inode, dir);
        if (err)
@@ -1074,10 +1069,10 @@ really_out:
        return ret;
 
 fail_free_drop:
-       vfs_dq_free_inode(inode);
+       dquot_free_inode(inode);
 
 fail_drop:
-       vfs_dq_drop(inode);
+       dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
        inode->i_nlink = 0;
        unlock_new_inode(inode);
@@ -1098,8 +1093,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
 
        /* Error cases - e2fsck has already cleaned up for us */
        if (ino > max_ino) {
-               ext4_warning(sb, __func__,
-                            "bad orphan ino %lu!  e2fsck was run?", ino);
+               ext4_warning(sb, "bad orphan ino %lu!  e2fsck was run?", ino);
                goto error;
        }
 
@@ -1107,8 +1101,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
        bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb);
        bitmap_bh = ext4_read_inode_bitmap(sb, block_group);
        if (!bitmap_bh) {
-               ext4_warning(sb, __func__,
-                            "inode bitmap error for orphan %lu", ino);
+               ext4_warning(sb, "inode bitmap error for orphan %lu", ino);
                goto error;
        }
 
@@ -1140,8 +1133,7 @@ iget_failed:
        err = PTR_ERR(inode);
        inode = NULL;
 bad_orphan:
-       ext4_warning(sb, __func__,
-                    "bad orphan inode %lu!  e2fsck was run?", ino);
+       ext4_warning(sb, "bad orphan inode %lu!  e2fsck was run?", ino);
        printk(KERN_NOTICE "ext4_test_bit(bit=%d, block=%llu) = %d\n",
               bit, (unsigned long long)bitmap_bh->b_blocknr,
               ext4_test_bit(bit, bitmap_bh->b_data));
index e11952404e02eea921717f4ff43721ffdc1e8857..986120f30066c68b68f7b40906a489cc2870810b 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/uio.h>
 #include <linux/bio.h>
 #include <linux/workqueue.h>
+#include <linux/kernel.h>
 
 #include "ext4_jbd2.h"
 #include "xattr.h"
@@ -170,6 +171,9 @@ void ext4_delete_inode(struct inode *inode)
        handle_t *handle;
        int err;
 
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
+
        if (ext4_should_order_data(inode))
                ext4_begin_ordered_truncate(inode, 0);
        truncate_inode_pages(&inode->i_data, 0);
@@ -194,7 +198,7 @@ void ext4_delete_inode(struct inode *inode)
        inode->i_size = 0;
        err = ext4_mark_inode_dirty(handle, inode);
        if (err) {
-               ext4_warning(inode->i_sb, __func__,
+               ext4_warning(inode->i_sb,
                             "couldn't mark inode dirty (err %d)", err);
                goto stop_handle;
        }
@@ -212,7 +216,7 @@ void ext4_delete_inode(struct inode *inode)
                if (err > 0)
                        err = ext4_journal_restart(handle, 3);
                if (err != 0) {
-                       ext4_warning(inode->i_sb, __func__,
+                       ext4_warning(inode->i_sb,
                                     "couldn't extend journal (err %d)", err);
                stop_handle:
                        ext4_journal_stop(handle);
@@ -323,8 +327,7 @@ static int ext4_block_to_path(struct inode *inode,
                offsets[n++] = i_block & (ptrs - 1);
                final = ptrs;
        } else {
-               ext4_warning(inode->i_sb, "ext4_block_to_path",
-                            "block %lu > max in inode %lu",
+               ext4_warning(inode->i_sb, "block %lu > max in inode %lu",
                             i_block + direct_blocks +
                             indirect_blocks + double_blocks, inode->i_ino);
        }
@@ -344,7 +347,7 @@ static int __ext4_check_blockref(const char *function, struct inode *inode,
                if (blk &&
                    unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
                                                    blk, 1))) {
-                       ext4_error(inode->i_sb, function,
+                       __ext4_error(inode->i_sb, function,
                                   "invalid block reference %u "
                                   "in inode #%lu", blk, inode->i_ino);
                        return -EIO;
@@ -607,7 +610,14 @@ static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,
                if (*err)
                        goto failed_out;
 
-               BUG_ON(current_block + count > EXT4_MAX_BLOCK_FILE_PHYS);
+               if (unlikely(current_block + count > EXT4_MAX_BLOCK_FILE_PHYS)) {
+                       EXT4_ERROR_INODE(inode,
+                                        "current_block %llu + count %lu > %d!",
+                                        current_block, count,
+                                        EXT4_MAX_BLOCK_FILE_PHYS);
+                       *err = -EIO;
+                       goto failed_out;
+               }
 
                target -= count;
                /* allocate blocks for indirect blocks */
@@ -643,7 +653,14 @@ static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,
                ar.flags = EXT4_MB_HINT_DATA;
 
        current_block = ext4_mb_new_blocks(handle, &ar, err);
-       BUG_ON(current_block + ar.len > EXT4_MAX_BLOCK_FILE_PHYS);
+       if (unlikely(current_block + ar.len > EXT4_MAX_BLOCK_FILE_PHYS)) {
+               EXT4_ERROR_INODE(inode,
+                                "current_block %llu + ar.len %d > %d!",
+                                current_block, ar.len,
+                                EXT4_MAX_BLOCK_FILE_PHYS);
+               *err = -EIO;
+               goto failed_out;
+       }
 
        if (*err && (target == blks)) {
                /*
@@ -1061,6 +1078,7 @@ void ext4_da_update_reserve_space(struct inode *inode,
        int mdb_free = 0, allocated_meta_blocks = 0;
 
        spin_lock(&ei->i_block_reservation_lock);
+       trace_ext4_da_update_reserve_space(inode, used);
        if (unlikely(used > ei->i_reserved_data_blocks)) {
                ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, used %d "
                         "with only %d reserved data blocks\n",
@@ -1093,9 +1111,9 @@ void ext4_da_update_reserve_space(struct inode *inode,
 
        /* Update quota subsystem */
        if (quota_claim) {
-               vfs_dq_claim_block(inode, used);
+               dquot_claim_block(inode, used);
                if (mdb_free)
-                       vfs_dq_release_reservation_block(inode, mdb_free);
+                       dquot_release_reservation_block(inode, mdb_free);
        } else {
                /*
                 * We did fallocate with an offset that is already delayed
@@ -1106,8 +1124,8 @@ void ext4_da_update_reserve_space(struct inode *inode,
                 * that
                 */
                if (allocated_meta_blocks)
-                       vfs_dq_claim_block(inode, allocated_meta_blocks);
-               vfs_dq_release_reservation_block(inode, mdb_free + used);
+                       dquot_claim_block(inode, allocated_meta_blocks);
+               dquot_release_reservation_block(inode, mdb_free + used);
        }
 
        /*
@@ -1124,7 +1142,7 @@ static int check_block_validity(struct inode *inode, const char *msg,
                                sector_t logical, sector_t phys, int len)
 {
        if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), phys, len)) {
-               ext4_error(inode->i_sb, msg,
+               __ext4_error(inode->i_sb, msg,
                           "inode #%lu logical block %llu mapped to %llu "
                           "(size %d)", inode->i_ino,
                           (unsigned long long) logical,
@@ -1306,7 +1324,7 @@ int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
                         * i_data's format changing.  Force the migrate
                         * to fail by clearing migrate flags
                         */
-                       EXT4_I(inode)->i_state &= ~EXT4_STATE_EXT_MIGRATE;
+                       ext4_clear_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
                }
 
                /*
@@ -1534,6 +1552,8 @@ static void ext4_truncate_failed_write(struct inode *inode)
        ext4_truncate(inode);
 }
 
+static int ext4_get_block_write(struct inode *inode, sector_t iblock,
+                  struct buffer_head *bh_result, int create);
 static int ext4_write_begin(struct file *file, struct address_space *mapping,
                            loff_t pos, unsigned len, unsigned flags,
                            struct page **pagep, void **fsdata)
@@ -1575,8 +1595,12 @@ retry:
        }
        *pagep = page;
 
-       ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
-                               ext4_get_block);
+       if (ext4_should_dioread_nolock(inode))
+               ret = block_write_begin(file, mapping, pos, len, flags, pagep,
+                               fsdata, ext4_get_block_write);
+       else
+               ret = block_write_begin(file, mapping, pos, len, flags, pagep,
+                               fsdata, ext4_get_block);
 
        if (!ret && ext4_should_journal_data(inode)) {
                ret = walk_page_buffers(handle, page_buffers(page),
@@ -1793,7 +1817,7 @@ static int ext4_journalled_write_end(struct file *file,
        new_i_size = pos + copied;
        if (new_i_size > inode->i_size)
                i_size_write(inode, pos+copied);
-       EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
+       ext4_set_inode_state(inode, EXT4_STATE_JDATA);
        if (new_i_size > EXT4_I(inode)->i_disksize) {
                ext4_update_i_disksize(inode, new_i_size);
                ret2 = ext4_mark_inode_dirty(handle, inode);
@@ -1836,6 +1860,7 @@ static int ext4_da_reserve_space(struct inode *inode, sector_t lblock)
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        struct ext4_inode_info *ei = EXT4_I(inode);
        unsigned long md_needed, md_reserved;
+       int ret;
 
        /*
         * recalculate the amount of metadata blocks to reserve
@@ -1846,6 +1871,7 @@ repeat:
        spin_lock(&ei->i_block_reservation_lock);
        md_reserved = ei->i_reserved_meta_blocks;
        md_needed = ext4_calc_metadata_amount(inode, lblock);
+       trace_ext4_da_reserve_space(inode, md_needed);
        spin_unlock(&ei->i_block_reservation_lock);
 
        /*
@@ -1853,11 +1879,12 @@ repeat:
         * later. Real quota accounting is done at pages writeout
         * time.
         */
-       if (vfs_dq_reserve_block(inode, md_needed + 1))
-               return -EDQUOT;
+       ret = dquot_reserve_block(inode, md_needed + 1);
+       if (ret)
+               return ret;
 
        if (ext4_claim_free_blocks(sbi, md_needed + 1)) {
-               vfs_dq_release_reservation_block(inode, md_needed + 1);
+               dquot_release_reservation_block(inode, md_needed + 1);
                if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
                        yield();
                        goto repeat;
@@ -1914,7 +1941,7 @@ static void ext4_da_release_space(struct inode *inode, int to_free)
 
        spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
 
-       vfs_dq_release_reservation_block(inode, to_free);
+       dquot_release_reservation_block(inode, to_free);
 }
 
 static void ext4_da_page_release_reservation(struct page *page,
@@ -2091,6 +2118,8 @@ static void mpage_put_bnr_to_bhs(struct mpage_da_data *mpd, sector_t logical,
                                } else if (buffer_mapped(bh))
                                        BUG_ON(bh->b_blocknr != pblock);
 
+                               if (buffer_uninit(exbh))
+                                       set_buffer_uninit(bh);
                                cur_logical++;
                                pblock++;
                        } while ((bh = bh->b_this_page) != head);
@@ -2133,17 +2162,16 @@ static void ext4_da_block_invalidatepages(struct mpage_da_data *mpd,
                        break;
                for (i = 0; i < nr_pages; i++) {
                        struct page *page = pvec.pages[i];
-                       index = page->index;
-                       if (index > end)
+                       if (page->index > end)
                                break;
-                       index++;
-
                        BUG_ON(!PageLocked(page));
                        BUG_ON(PageWriteback(page));
                        block_invalidatepage(page, 0);
                        ClearPageUptodate(page);
                        unlock_page(page);
                }
+               index = pvec.pages[nr_pages - 1]->index + 1;
+               pagevec_release(&pvec);
        }
        return;
 }
@@ -2220,6 +2248,8 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd)
         */
        new.b_state = 0;
        get_blocks_flags = EXT4_GET_BLOCKS_CREATE;
+       if (ext4_should_dioread_nolock(mpd->inode))
+               get_blocks_flags |= EXT4_GET_BLOCKS_IO_CREATE_EXT;
        if (mpd->b_state & (1 << BH_Delay))
                get_blocks_flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE;
 
@@ -2630,11 +2660,14 @@ static int __ext4_journalled_writepage(struct page *page,
                ret = err;
 
        walk_page_buffers(handle, page_bufs, 0, len, NULL, bput_one);
-       EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
+       ext4_set_inode_state(inode, EXT4_STATE_JDATA);
 out:
        return ret;
 }
 
+static int ext4_set_bh_endio(struct buffer_head *bh, struct inode *inode);
+static void ext4_end_io_buffer_write(struct buffer_head *bh, int uptodate);
+
 /*
  * Note that we don't need to start a transaction unless we're journaling data
  * because we should have holes filled from ext4_page_mkwrite(). We even don't
@@ -2682,7 +2715,7 @@ static int ext4_writepage(struct page *page,
        int ret = 0;
        loff_t size;
        unsigned int len;
-       struct buffer_head *page_bufs;
+       struct buffer_head *page_bufs = NULL;
        struct inode *inode = page->mapping->host;
 
        trace_ext4_writepage(inode, page);
@@ -2758,7 +2791,11 @@ static int ext4_writepage(struct page *page,
 
        if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
                ret = nobh_writepage(page, noalloc_get_block_write, wbc);
-       else
+       else if (page_bufs && buffer_uninit(page_bufs)) {
+               ext4_set_bh_endio(page_bufs, inode);
+               ret = block_write_full_page_endio(page, noalloc_get_block_write,
+                                           wbc, ext4_end_io_buffer_write);
+       } else
                ret = block_write_full_page(page, noalloc_get_block_write,
                                            wbc);
 
@@ -3301,7 +3338,8 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block)
                filemap_write_and_wait(mapping);
        }
 
-       if (EXT4_JOURNAL(inode) && EXT4_I(inode)->i_state & EXT4_STATE_JDATA) {
+       if (EXT4_JOURNAL(inode) &&
+           ext4_test_inode_state(inode, EXT4_STATE_JDATA)) {
                /*
                 * This is a REALLY heavyweight approach, but the use of
                 * bmap on dirty files is expected to be extremely rare:
@@ -3320,7 +3358,7 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block)
                 * everything they get.
                 */
 
-               EXT4_I(inode)->i_state &= ~EXT4_STATE_JDATA;
+               ext4_clear_inode_state(inode, EXT4_STATE_JDATA);
                journal = EXT4_JOURNAL(inode);
                jbd2_journal_lock_updates(journal);
                err = jbd2_journal_flush(journal);
@@ -3345,10 +3383,44 @@ ext4_readpages(struct file *file, struct address_space *mapping,
        return mpage_readpages(mapping, pages, nr_pages, ext4_get_block);
 }
 
+static void ext4_free_io_end(ext4_io_end_t *io)
+{
+       BUG_ON(!io);
+       if (io->page)
+               put_page(io->page);
+       iput(io->inode);
+       kfree(io);
+}
+
+static void ext4_invalidatepage_free_endio(struct page *page, unsigned long offset)
+{
+       struct buffer_head *head, *bh;
+       unsigned int curr_off = 0;
+
+       if (!page_has_buffers(page))
+               return;
+       head = bh = page_buffers(page);
+       do {
+               if (offset <= curr_off && test_clear_buffer_uninit(bh)
+                                       && bh->b_private) {
+                       ext4_free_io_end(bh->b_private);
+                       bh->b_private = NULL;
+                       bh->b_end_io = NULL;
+               }
+               curr_off = curr_off + bh->b_size;
+               bh = bh->b_this_page;
+       } while (bh != head);
+}
+
 static void ext4_invalidatepage(struct page *page, unsigned long offset)
 {
        journal_t *journal = EXT4_JOURNAL(page->mapping->host);
 
+       /*
+        * free any io_end structure allocated for buffers to be discarded
+        */
+       if (ext4_should_dioread_nolock(page->mapping->host))
+               ext4_invalidatepage_free_endio(page, offset);
        /*
         * If it's a full truncate we just forget about the pending dirtying
         */
@@ -3420,7 +3492,14 @@ static ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
        }
 
 retry:
-       ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
+       if (rw == READ && ext4_should_dioread_nolock(inode))
+               ret = blockdev_direct_IO_no_locking(rw, iocb, inode,
+                                inode->i_sb->s_bdev, iov,
+                                offset, nr_segs,
+                                ext4_get_block, NULL);
+       else
+               ret = blockdev_direct_IO(rw, iocb, inode,
+                                inode->i_sb->s_bdev, iov,
                                 offset, nr_segs,
                                 ext4_get_block, NULL);
        if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
@@ -3436,6 +3515,9 @@ retry:
                         * but cannot extend i_size. Bail out and pretend
                         * the write failed... */
                        ret = PTR_ERR(handle);
+                       if (inode->i_nlink)
+                               ext4_orphan_del(NULL, inode);
+
                        goto out;
                }
                if (inode->i_nlink)
@@ -3463,75 +3545,63 @@ out:
        return ret;
 }
 
-static int ext4_get_block_dio_write(struct inode *inode, sector_t iblock,
+static int ext4_get_block_write(struct inode *inode, sector_t iblock,
                   struct buffer_head *bh_result, int create)
 {
-       handle_t *handle = NULL;
+       handle_t *handle = ext4_journal_current_handle();
        int ret = 0;
        unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
        int dio_credits;
+       int started = 0;
 
-       ext4_debug("ext4_get_block_dio_write: inode %lu, create flag %d\n",
+       ext4_debug("ext4_get_block_write: inode %lu, create flag %d\n",
                   inode->i_ino, create);
        /*
-        * DIO VFS code passes create = 0 flag for write to
-        * the middle of file. It does this to avoid block
-        * allocation for holes, to prevent expose stale data
-        * out when there is parallel buffered read (which does
-        * not hold the i_mutex lock) while direct IO write has
-        * not completed. DIO request on holes finally falls back
-        * to buffered IO for this reason.
-        *
-        * For ext4 extent based file, since we support fallocate,
-        * new allocated extent as uninitialized, for holes, we
-        * could fallocate blocks for holes, thus parallel
-        * buffered IO read will zero out the page when read on
-        * a hole while parallel DIO write to the hole has not completed.
-        *
-        * when we come here, we know it's a direct IO write to
-        * to the middle of file (<i_size)
-        * so it's safe to override the create flag from VFS.
+        * ext4_get_block in prepare for a DIO write or buffer write.
+        * We allocate an uinitialized extent if blocks haven't been allocated.
+        * The extent will be converted to initialized after IO complete.
         */
-       create = EXT4_GET_BLOCKS_DIO_CREATE_EXT;
+       create = EXT4_GET_BLOCKS_IO_CREATE_EXT;
 
-       if (max_blocks > DIO_MAX_BLOCKS)
-               max_blocks = DIO_MAX_BLOCKS;
-       dio_credits = ext4_chunk_trans_blocks(inode, max_blocks);
-       handle = ext4_journal_start(inode, dio_credits);
-       if (IS_ERR(handle)) {
-               ret = PTR_ERR(handle);
-               goto out;
+       if (!handle) {
+               if (max_blocks > DIO_MAX_BLOCKS)
+                       max_blocks = DIO_MAX_BLOCKS;
+               dio_credits = ext4_chunk_trans_blocks(inode, max_blocks);
+               handle = ext4_journal_start(inode, dio_credits);
+               if (IS_ERR(handle)) {
+                       ret = PTR_ERR(handle);
+                       goto out;
+               }
+               started = 1;
        }
+
        ret = ext4_get_blocks(handle, inode, iblock, max_blocks, bh_result,
                              create);
        if (ret > 0) {
                bh_result->b_size = (ret << inode->i_blkbits);
                ret = 0;
        }
-       ext4_journal_stop(handle);
+       if (started)
+               ext4_journal_stop(handle);
 out:
        return ret;
 }
 
-static void ext4_free_io_end(ext4_io_end_t *io)
-{
-       BUG_ON(!io);
-       iput(io->inode);
-       kfree(io);
-}
-static void dump_aio_dio_list(struct inode * inode)
+static void dump_completed_IO(struct inode * inode)
 {
 #ifdef EXT4_DEBUG
        struct list_head *cur, *before, *after;
        ext4_io_end_t *io, *io0, *io1;
+       unsigned long flags;
 
-       if (list_empty(&EXT4_I(inode)->i_aio_dio_complete_list)){
-               ext4_debug("inode %lu aio dio list is empty\n", inode->i_ino);
+       if (list_empty(&EXT4_I(inode)->i_completed_io_list)){
+               ext4_debug("inode %lu completed_io list is empty\n", inode->i_ino);
                return;
        }
 
-       ext4_debug("Dump inode %lu aio_dio_completed_IO list \n", inode->i_ino);
-       list_for_each_entry(io, &EXT4_I(inode)->i_aio_dio_complete_list, list){
+       ext4_debug("Dump inode %lu completed_io list \n", inode->i_ino);
+       spin_lock_irqsave(&EXT4_I(inode)->i_completed_io_lock, flags);
+       list_for_each_entry(io, &EXT4_I(inode)->i_completed_io_list, list){
                cur = &io->list;
                before = cur->prev;
                io0 = container_of(before, ext4_io_end_t, list);
@@ -3541,32 +3611,31 @@ static void dump_aio_dio_list(struct inode * inode)
                ext4_debug("io 0x%p from inode %lu,prev 0x%p,next 0x%p\n",
                            io, inode->i_ino, io0, io1);
        }
+       spin_unlock_irqrestore(&EXT4_I(inode)->i_completed_io_lock, flags);
 #endif
 }
 
 /*
  * check a range of space and convert unwritten extents to written.
  */
-static int ext4_end_aio_dio_nolock(ext4_io_end_t *io)
+static int ext4_end_io_nolock(ext4_io_end_t *io)
 {
        struct inode *inode = io->inode;
        loff_t offset = io->offset;
-       size_t size = io->size;
+       ssize_t size = io->size;
        int ret = 0;
 
-       ext4_debug("end_aio_dio_onlock: io 0x%p from inode %lu,list->next 0x%p,"
+       ext4_debug("ext4_end_io_nolock: io 0x%p from inode %lu,list->next 0x%p,"
                   "list->prev 0x%p\n",
                   io, inode->i_ino, io->list.next, io->list.prev);
 
        if (list_empty(&io->list))
                return ret;
 
-       if (io->flag != DIO_AIO_UNWRITTEN)
+       if (io->flag != EXT4_IO_UNWRITTEN)
                return ret;
 
-       if (offset + size <= i_size_read(inode))
-               ret = ext4_convert_unwritten_extents(inode, offset, size);
-
+       ret = ext4_convert_unwritten_extents(inode, offset, size);
        if (ret < 0) {
                printk(KERN_EMERG "%s: failed to convert unwritten"
                        "extents to written extents, error is %d"
@@ -3579,50 +3648,64 @@ static int ext4_end_aio_dio_nolock(ext4_io_end_t *io)
        io->flag = 0;
        return ret;
 }
+
 /*
  * work on completed aio dio IO, to convert unwritten extents to extents
  */
-static void ext4_end_aio_dio_work(struct work_struct *work)
+static void ext4_end_io_work(struct work_struct *work)
 {
-       ext4_io_end_t *io  = container_of(work, ext4_io_end_t, work);
-       struct inode *inode = io->inode;
-       int ret = 0;
+       ext4_io_end_t           *io = container_of(work, ext4_io_end_t, work);
+       struct inode            *inode = io->inode;
+       struct ext4_inode_info  *ei = EXT4_I(inode);
+       unsigned long           flags;
+       int                     ret;
 
        mutex_lock(&inode->i_mutex);
-       ret = ext4_end_aio_dio_nolock(io);
-       if (ret >= 0) {
-               if (!list_empty(&io->list))
-                       list_del_init(&io->list);
-               ext4_free_io_end(io);
+       ret = ext4_end_io_nolock(io);
+       if (ret < 0) {
+               mutex_unlock(&inode->i_mutex);
+               return;
        }
+
+       spin_lock_irqsave(&ei->i_completed_io_lock, flags);
+       if (!list_empty(&io->list))
+               list_del_init(&io->list);
+       spin_unlock_irqrestore(&ei->i_completed_io_lock, flags);
        mutex_unlock(&inode->i_mutex);
+       ext4_free_io_end(io);
 }
+
 /*
  * This function is called from ext4_sync_file().
  *
- * When AIO DIO IO is completed, the work to convert unwritten
- * extents to written is queued on workqueue but may not get immediately
+ * When IO is completed, the work to convert unwritten extents to
+ * written is queued on workqueue but may not get immediately
  * scheduled. When fsync is called, we need to ensure the
  * conversion is complete before fsync returns.
- * The inode keeps track of a list of completed AIO from DIO path
- * that might needs to do the conversion. This function walks through
- * the list and convert the related unwritten extents to written.
+ * The inode keeps track of a list of pending/completed IO that
+ * might needs to do the conversion. This function walks through
+ * the list and convert the related unwritten extents for completed IO
+ * to written.
+ * The function return the number of pending IOs on success.
  */
-int flush_aio_dio_completed_IO(struct inode *inode)
+int flush_completed_IO(struct inode *inode)
 {
        ext4_io_end_t *io;
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       unsigned long flags;
        int ret = 0;
        int ret2 = 0;
 
-       if (list_empty(&EXT4_I(inode)->i_aio_dio_complete_list))
+       if (list_empty(&ei->i_completed_io_list))
                return ret;
 
-       dump_aio_dio_list(inode);
-       while (!list_empty(&EXT4_I(inode)->i_aio_dio_complete_list)){
-               io = list_entry(EXT4_I(inode)->i_aio_dio_complete_list.next,
+       dump_completed_IO(inode);
+       spin_lock_irqsave(&ei->i_completed_io_lock, flags);
+       while (!list_empty(&ei->i_completed_io_list)){
+               io = list_entry(ei->i_completed_io_list.next,
                                ext4_io_end_t, list);
                /*
-                * Calling ext4_end_aio_dio_nolock() to convert completed
+                * Calling ext4_end_io_nolock() to convert completed
                 * IO to written.
                 *
                 * When ext4_sync_file() is called, run_queue() may already
@@ -3635,20 +3718,23 @@ int flush_aio_dio_completed_IO(struct inode *inode)
                 * avoid double converting from both fsync and background work
                 * queue work.
                 */
-               ret = ext4_end_aio_dio_nolock(io);
+               spin_unlock_irqrestore(&ei->i_completed_io_lock, flags);
+               ret = ext4_end_io_nolock(io);
+               spin_lock_irqsave(&ei->i_completed_io_lock, flags);
                if (ret < 0)
                        ret2 = ret;
                else
                        list_del_init(&io->list);
        }
+       spin_unlock_irqrestore(&ei->i_completed_io_lock, flags);
        return (ret2 < 0) ? ret2 : 0;
 }
 
-static ext4_io_end_t *ext4_init_io_end (struct inode *inode)
+static ext4_io_end_t *ext4_init_io_end (struct inode *inode, gfp_t flags)
 {
        ext4_io_end_t *io = NULL;
 
-       io = kmalloc(sizeof(*io), GFP_NOFS);
+       io = kmalloc(sizeof(*io), flags);
 
        if (io) {
                igrab(inode);
@@ -3656,8 +3742,8 @@ static ext4_io_end_t *ext4_init_io_end (struct inode *inode)
                io->flag = 0;
                io->offset = 0;
                io->size = 0;
-               io->error = 0;
-               INIT_WORK(&io->work, ext4_end_aio_dio_work);
+               io->page = NULL;
+               INIT_WORK(&io->work, ext4_end_io_work);
                INIT_LIST_HEAD(&io->list);
        }
 
@@ -3669,6 +3755,8 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
 {
         ext4_io_end_t *io_end = iocb->private;
        struct workqueue_struct *wq;
+       unsigned long flags;
+       struct ext4_inode_info *ei;
 
        /* if not async direct IO or dio with 0 bytes write, just return */
        if (!io_end || !size)
@@ -3680,7 +3768,7 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
                  size);
 
        /* if not aio dio with unwritten extents, just free io and return */
-       if (io_end->flag != DIO_AIO_UNWRITTEN){
+       if (io_end->flag != EXT4_IO_UNWRITTEN){
                ext4_free_io_end(io_end);
                iocb->private = NULL;
                return;
@@ -3688,16 +3776,85 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
 
        io_end->offset = offset;
        io_end->size = size;
+       io_end->flag = EXT4_IO_UNWRITTEN;
        wq = EXT4_SB(io_end->inode->i_sb)->dio_unwritten_wq;
 
        /* queue the work to convert unwritten extents to written */
        queue_work(wq, &io_end->work);
 
        /* Add the io_end to per-inode completed aio dio list*/
-       list_add_tail(&io_end->list,
-                &EXT4_I(io_end->inode)->i_aio_dio_complete_list);
+       ei = EXT4_I(io_end->inode);
+       spin_lock_irqsave(&ei->i_completed_io_lock, flags);
+       list_add_tail(&io_end->list, &ei->i_completed_io_list);
+       spin_unlock_irqrestore(&ei->i_completed_io_lock, flags);
        iocb->private = NULL;
 }
+
+static void ext4_end_io_buffer_write(struct buffer_head *bh, int uptodate)
+{
+       ext4_io_end_t *io_end = bh->b_private;
+       struct workqueue_struct *wq;
+       struct inode *inode;
+       unsigned long flags;
+
+       if (!test_clear_buffer_uninit(bh) || !io_end)
+               goto out;
+
+       if (!(io_end->inode->i_sb->s_flags & MS_ACTIVE)) {
+               printk("sb umounted, discard end_io request for inode %lu\n",
+                       io_end->inode->i_ino);
+               ext4_free_io_end(io_end);
+               goto out;
+       }
+
+       io_end->flag = EXT4_IO_UNWRITTEN;
+       inode = io_end->inode;
+
+       /* Add the io_end to per-inode completed io list*/
+       spin_lock_irqsave(&EXT4_I(inode)->i_completed_io_lock, flags);
+       list_add_tail(&io_end->list, &EXT4_I(inode)->i_completed_io_list);
+       spin_unlock_irqrestore(&EXT4_I(inode)->i_completed_io_lock, flags);
+
+       wq = EXT4_SB(inode->i_sb)->dio_unwritten_wq;
+       /* queue the work to convert unwritten extents to written */
+       queue_work(wq, &io_end->work);
+out:
+       bh->b_private = NULL;
+       bh->b_end_io = NULL;
+       clear_buffer_uninit(bh);
+       end_buffer_async_write(bh, uptodate);
+}
+
+static int ext4_set_bh_endio(struct buffer_head *bh, struct inode *inode)
+{
+       ext4_io_end_t *io_end;
+       struct page *page = bh->b_page;
+       loff_t offset = (sector_t)page->index << PAGE_CACHE_SHIFT;
+       size_t size = bh->b_size;
+
+retry:
+       io_end = ext4_init_io_end(inode, GFP_ATOMIC);
+       if (!io_end) {
+               if (printk_ratelimit())
+                       printk(KERN_WARNING "%s: allocation fail\n", __func__);
+               schedule();
+               goto retry;
+       }
+       io_end->offset = offset;
+       io_end->size = size;
+       /*
+        * We need to hold a reference to the page to make sure it
+        * doesn't get evicted before ext4_end_io_work() has a chance
+        * to convert the extent from written to unwritten.
+        */
+       io_end->page = page;
+       get_page(io_end->page);
+
+       bh->b_private = io_end;
+       bh->b_end_io = ext4_end_io_buffer_write;
+       return 0;
+}
+
 /*
  * For ext4 extent files, ext4 will do direct-io write to holes,
  * preallocated extents, and those write extend the file, no need to
@@ -3751,7 +3908,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
                iocb->private = NULL;
                EXT4_I(inode)->cur_aio_dio = NULL;
                if (!is_sync_kiocb(iocb)) {
-                       iocb->private = ext4_init_io_end(inode);
+                       iocb->private = ext4_init_io_end(inode, GFP_NOFS);
                        if (!iocb->private)
                                return -ENOMEM;
                        /*
@@ -3767,7 +3924,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
                ret = blockdev_direct_IO(rw, iocb, inode,
                                         inode->i_sb->s_bdev, iov,
                                         offset, nr_segs,
-                                        ext4_get_block_dio_write,
+                                        ext4_get_block_write,
                                         ext4_end_io_dio);
                if (iocb->private)
                        EXT4_I(inode)->cur_aio_dio = NULL;
@@ -3788,8 +3945,8 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
                if (ret != -EIOCBQUEUED && ret <= 0 && iocb->private) {
                        ext4_free_io_end(iocb->private);
                        iocb->private = NULL;
-               } else if (ret > 0 && (EXT4_I(inode)->i_state &
-                                      EXT4_STATE_DIO_UNWRITTEN)) {
+               } else if (ret > 0 && ext4_test_inode_state(inode,
+                                               EXT4_STATE_DIO_UNWRITTEN)) {
                        int err;
                        /*
                         * for non AIO case, since the IO is already
@@ -3799,7 +3956,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb,
                                                             offset, ret);
                        if (err < 0)
                                ret = err;
-                       EXT4_I(inode)->i_state &= ~EXT4_STATE_DIO_UNWRITTEN;
+                       ext4_clear_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN);
                }
                return ret;
        }
@@ -4130,18 +4287,27 @@ no_top:
  * We release `count' blocks on disk, but (last - first) may be greater
  * than `count' because there can be holes in there.
  */
-static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
-                             struct buffer_head *bh,
-                             ext4_fsblk_t block_to_free,
-                             unsigned long count, __le32 *first,
-                             __le32 *last)
+static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
+                            struct buffer_head *bh,
+                            ext4_fsblk_t block_to_free,
+                            unsigned long count, __le32 *first,
+                            __le32 *last)
 {
        __le32 *p;
-       int     flags = EXT4_FREE_BLOCKS_FORGET;
+       int     flags = EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_VALIDATED;
 
        if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
                flags |= EXT4_FREE_BLOCKS_METADATA;
 
+       if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
+                                  count)) {
+               ext4_error(inode->i_sb, "inode #%lu: "
+                          "attempt to clear blocks %llu len %lu, invalid",
+                          inode->i_ino, (unsigned long long) block_to_free,
+                          count);
+               return 1;
+       }
+
        if (try_to_extend_transaction(handle, inode)) {
                if (bh) {
                        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
@@ -4160,6 +4326,7 @@ static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
                *p = 0;
 
        ext4_free_blocks(handle, inode, 0, block_to_free, count, flags);
+       return 0;
 }
 
 /**
@@ -4215,9 +4382,10 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,
                        } else if (nr == block_to_free + count) {
                                count++;
                        } else {
-                               ext4_clear_blocks(handle, inode, this_bh,
-                                                 block_to_free,
-                                                 count, block_to_free_p, p);
+                               if (ext4_clear_blocks(handle, inode, this_bh,
+                                                     block_to_free, count,
+                                                     block_to_free_p, p))
+                                       break;
                                block_to_free = nr;
                                block_to_free_p = p;
                                count = 1;
@@ -4241,7 +4409,7 @@ static void ext4_free_data(handle_t *handle, struct inode *inode,
                if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
                        ext4_handle_dirty_metadata(handle, inode, this_bh);
                else
-                       ext4_error(inode->i_sb, __func__,
+                       ext4_error(inode->i_sb,
                                   "circular indirect block detected, "
                                   "inode=%lu, block=%llu",
                                   inode->i_ino,
@@ -4281,6 +4449,16 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
                        if (!nr)
                                continue;               /* A hole */
 
+                       if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
+                                                  nr, 1)) {
+                               ext4_error(inode->i_sb,
+                                          "indirect mapped block in inode "
+                                          "#%lu invalid (level %d, blk #%lu)",
+                                          inode->i_ino, depth,
+                                          (unsigned long) nr);
+                               break;
+                       }
+
                        /* Go read the buffer for the next level down */
                        bh = sb_bread(inode->i_sb, nr);
 
@@ -4289,7 +4467,7 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
                         * (should be rare).
                         */
                        if (!bh) {
-                               ext4_error(inode->i_sb, "ext4_free_branches",
+                               ext4_error(inode->i_sb,
                                           "Read failure, inode=%lu, block=%llu",
                                           inode->i_ino, nr);
                                continue;
@@ -4433,8 +4611,10 @@ void ext4_truncate(struct inode *inode)
        if (!ext4_can_truncate(inode))
                return;
 
+       EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL;
+
        if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
-               ei->i_state |= EXT4_STATE_DA_ALLOC_CLOSE;
+               ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
 
        if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
                ext4_ext_truncate(inode);
@@ -4604,9 +4784,8 @@ static int __ext4_get_inode_loc(struct inode *inode,
 
        bh = sb_getblk(sb, block);
        if (!bh) {
-               ext4_error(sb, "ext4_get_inode_loc", "unable to read "
-                          "inode block - inode=%lu, block=%llu",
-                          inode->i_ino, block);
+               ext4_error(sb, "unable to read inode block - "
+                          "inode=%lu, block=%llu", inode->i_ino, block);
                return -EIO;
        }
        if (!buffer_uptodate(bh)) {
@@ -4704,9 +4883,8 @@ make_io:
                submit_bh(READ_META, bh);
                wait_on_buffer(bh);
                if (!buffer_uptodate(bh)) {
-                       ext4_error(sb, __func__,
-                                  "unable to read inode block - inode=%lu, "
-                                  "block=%llu", inode->i_ino, block);
+                       ext4_error(sb, "unable to read inode block - inode=%lu,"
+                                  " block=%llu", inode->i_ino, block);
                        brelse(bh);
                        return -EIO;
                }
@@ -4720,7 +4898,7 @@ int ext4_get_inode_loc(struct inode *inode, struct ext4_iloc *iloc)
 {
        /* We have all inode data except xattrs in memory here. */
        return __ext4_get_inode_loc(inode, iloc,
-               !(EXT4_I(inode)->i_state & EXT4_STATE_XATTR));
+               !ext4_test_inode_state(inode, EXT4_STATE_XATTR));
 }
 
 void ext4_set_inode_flags(struct inode *inode)
@@ -4814,7 +4992,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        }
        inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
 
-       ei->i_state = 0;
+       ei->i_state_flags = 0;
        ei->i_dir_start_lookup = 0;
        ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
        /* We now have enough fields to check if the inode was active or not.
@@ -4897,7 +5075,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                                        EXT4_GOOD_OLD_INODE_SIZE +
                                        ei->i_extra_isize;
                        if (*magic == cpu_to_le32(EXT4_XATTR_MAGIC))
-                               ei->i_state |= EXT4_STATE_XATTR;
+                               ext4_set_inode_state(inode, EXT4_STATE_XATTR);
                }
        } else
                ei->i_extra_isize = 0;
@@ -4917,8 +5095,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        ret = 0;
        if (ei->i_file_acl &&
            !ext4_data_block_valid(EXT4_SB(sb), ei->i_file_acl, 1)) {
-               ext4_error(sb, __func__,
-                          "bad extended attribute block %llu in inode #%lu",
+               ext4_error(sb, "bad extended attribute block %llu inode #%lu",
                           ei->i_file_acl, inode->i_ino);
                ret = -EIO;
                goto bad_inode;
@@ -4964,8 +5141,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                           new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
        } else {
                ret = -EIO;
-               ext4_error(inode->i_sb, __func__,
-                          "bogus i_mode (%o) for inode=%lu",
+               ext4_error(inode->i_sb, "bogus i_mode (%o) for inode=%lu",
                           inode->i_mode, inode->i_ino);
                goto bad_inode;
        }
@@ -5037,7 +5213,7 @@ static int ext4_do_update_inode(handle_t *handle,
 
        /* For fields not not tracking in the in-memory inode,
         * initialise them to zero for new inodes. */
-       if (ei->i_state & EXT4_STATE_NEW)
+       if (ext4_test_inode_state(inode, EXT4_STATE_NEW))
                memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size);
 
        ext4_get_inode_flags(ei);
@@ -5101,7 +5277,7 @@ static int ext4_do_update_inode(handle_t *handle,
                                        EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
                        sb->s_dirt = 1;
                        ext4_handle_sync(handle);
-                       err = ext4_handle_dirty_metadata(handle, inode,
+                       err = ext4_handle_dirty_metadata(handle, NULL,
                                        EXT4_SB(sb)->s_sbh);
                }
        }
@@ -5130,10 +5306,10 @@ static int ext4_do_update_inode(handle_t *handle,
        }
 
        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
-       rc = ext4_handle_dirty_metadata(handle, inode, bh);
+       rc = ext4_handle_dirty_metadata(handle, NULL, bh);
        if (!err)
                err = rc;
-       ei->i_state &= ~EXT4_STATE_NEW;
+       ext4_clear_inode_state(inode, EXT4_STATE_NEW);
 
        ext4_update_inode_fsync_trans(handle, inode, 0);
 out_brelse:
@@ -5177,7 +5353,7 @@ out_brelse:
  * `stuff()' is running, and the new i_size will be lost.  Plus the inode
  * will no longer be on the superblock's dirty inode list.
  */
-int ext4_write_inode(struct inode *inode, int wait)
+int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        int err;
 
@@ -5191,7 +5367,7 @@ int ext4_write_inode(struct inode *inode, int wait)
                        return -EIO;
                }
 
-               if (!wait)
+               if (wbc->sync_mode != WB_SYNC_ALL)
                        return 0;
 
                err = ext4_force_commit(inode->i_sb);
@@ -5201,13 +5377,11 @@ int ext4_write_inode(struct inode *inode, int wait)
                err = ext4_get_inode_loc(inode, &iloc);
                if (err)
                        return err;
-               if (wait)
+               if (wbc->sync_mode == WB_SYNC_ALL)
                        sync_dirty_buffer(iloc.bh);
                if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
-                       ext4_error(inode->i_sb, __func__,
-                                  "IO error syncing inode, "
-                                  "inode=%lu, block=%llu",
-                                  inode->i_ino,
+                       ext4_error(inode->i_sb, "IO error syncing inode, "
+                                  "inode=%lu, block=%llu", inode->i_ino,
                                   (unsigned long long)iloc.bh->b_blocknr);
                        err = -EIO;
                }
@@ -5249,6 +5423,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
        if (error)
                return error;
 
+       if (ia_valid & ATTR_SIZE)
+               dquot_initialize(inode);
        if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
                (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
                handle_t *handle;
@@ -5261,7 +5437,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
                        error = PTR_ERR(handle);
                        goto err_out;
                }
-               error = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
+               error = dquot_transfer(inode, attr);
                if (error) {
                        ext4_journal_stop(handle);
                        return error;
@@ -5288,7 +5464,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
        }
 
        if (S_ISREG(inode->i_mode) &&
-           attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) {
+           attr->ia_valid & ATTR_SIZE &&
+           (attr->ia_size < inode->i_size ||
+            (EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))) {
                handle_t *handle;
 
                handle = ext4_journal_start(inode, 3);
@@ -5319,6 +5497,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
                                goto err_out;
                        }
                }
+               /* ext4_truncate will clear the flag */
+               if ((EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))
+                       ext4_truncate(inode);
        }
 
        rc = inode_setattr(inode, attr);
@@ -5557,8 +5738,8 @@ static int ext4_expand_extra_isize(struct inode *inode,
        entry = IFIRST(header);
 
        /* No extended attributes present */
-       if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
-               header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
+       if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
+           header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
                memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
                        new_extra_isize);
                EXT4_I(inode)->i_extra_isize = new_extra_isize;
@@ -5602,7 +5783,7 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
        err = ext4_reserve_inode_write(handle, inode, &iloc);
        if (ext4_handle_valid(handle) &&
            EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize &&
-           !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) {
+           !ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) {
                /*
                 * We need extra buffer credits since we may write into EA block
                 * with this same handle. If journal_extend fails, then it will
@@ -5616,10 +5797,11 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
                                                      sbi->s_want_extra_isize,
                                                      iloc, handle);
                        if (ret) {
-                               EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND;
+                               ext4_set_inode_state(inode,
+                                                    EXT4_STATE_NO_EXPAND);
                                if (mnt_count !=
                                        le16_to_cpu(sbi->s_es->s_mnt_count)) {
-                                       ext4_warning(inode->i_sb, __func__,
+                                       ext4_warning(inode->i_sb,
                                        "Unable to expand inode %lu. Delete"
                                        " some EAs or run e2fsck.",
                                        inode->i_ino);
@@ -5641,7 +5823,7 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
  * i_size has been changed by generic_commit_write() and we thus need
  * to include the updated inode in the current transaction.
  *
- * Also, vfs_dq_alloc_block() will always dirty the inode when blocks
+ * Also, dquot_alloc_block() will always dirty the inode when blocks
  * are allocated to the file.
  *
  * If the inode is marked synchronous, we don't honour that here - doing
@@ -5683,7 +5865,7 @@ static int ext4_pin_inode(handle_t *handle, struct inode *inode)
                        err = jbd2_journal_get_write_access(handle, iloc.bh);
                        if (!err)
                                err = ext4_handle_dirty_metadata(handle,
-                                                                inode,
+                                                                NULL,
                                                                 iloc.bh);
                        brelse(iloc.bh);
                }
index b63d193126dbf759236a52f075af914bc921d40a..016d0249294ff15d3442061d38050df24b09e50e 100644 (file)
@@ -92,6 +92,15 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                        flags &= ~EXT4_EXTENTS_FL;
                }
 
+               if (flags & EXT4_EOFBLOCKS_FL) {
+                       /* we don't support adding EOFBLOCKS flag */
+                       if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
+                               err = -EOPNOTSUPP;
+                               goto flags_out;
+                       }
+               } else if (oldflags & EXT4_EOFBLOCKS_FL)
+                       ext4_truncate(inode);
+
                handle = ext4_journal_start(inode, 1);
                if (IS_ERR(handle)) {
                        err = PTR_ERR(handle);
@@ -249,7 +258,8 @@ setversion_out:
                if (me.moved_len > 0)
                        file_remove_suid(donor_filp);
 
-               if (copy_to_user((struct move_extent *)arg, &me, sizeof(me)))
+               if (copy_to_user((struct move_extent __user *)arg, 
+                                &me, sizeof(me)))
                        err = -EFAULT;
 mext_out:
                fput(donor_filp);
index d34afad3e1371b89c3bfb600c51d55e714f00419..506713a2ebd8c297f1d855cb5581c750082af95f 100644 (file)
@@ -441,10 +441,9 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b,
        for (i = 0; i < count; i++) {
                if (!mb_test_bit(first + i, e4b->bd_info->bb_bitmap)) {
                        ext4_fsblk_t blocknr;
-                       blocknr = e4b->bd_group * EXT4_BLOCKS_PER_GROUP(sb);
+
+                       blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
                        blocknr += first + i;
-                       blocknr +=
-                           le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
                        ext4_grp_locked_error(sb, e4b->bd_group,
                                   __func__, "double-free of inode"
                                   " %lu's block %llu(bit %u in group %u)",
@@ -1255,10 +1254,9 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
 
                if (!mb_test_bit(block, EXT4_MB_BITMAP(e4b))) {
                        ext4_fsblk_t blocknr;
-                       blocknr = e4b->bd_group * EXT4_BLOCKS_PER_GROUP(sb);
+
+                       blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
                        blocknr += block;
-                       blocknr +=
-                           le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
                        ext4_grp_locked_error(sb, e4b->bd_group,
                                   __func__, "double-free of inode"
                                   " %lu's block %llu(bit %u in group %u)",
@@ -1631,7 +1629,6 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac,
        int max;
        int err;
        struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb);
-       struct ext4_super_block *es = sbi->s_es;
        struct ext4_free_extent ex;
 
        if (!(ac->ac_flags & EXT4_MB_HINT_TRY_GOAL))
@@ -1648,8 +1645,8 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac,
        if (max >= ac->ac_g_ex.fe_len && ac->ac_g_ex.fe_len == sbi->s_stripe) {
                ext4_fsblk_t start;
 
-               start = (e4b->bd_group * EXT4_BLOCKS_PER_GROUP(ac->ac_sb)) +
-                       ex.fe_start + le32_to_cpu(es->s_first_data_block);
+               start = ext4_group_first_block_no(ac->ac_sb, e4b->bd_group) +
+                       ex.fe_start;
                /* use do_div to get remainder (would be 64-bit modulo) */
                if (do_div(start, sbi->s_stripe) == 0) {
                        ac->ac_found++;
@@ -1803,8 +1800,8 @@ void ext4_mb_scan_aligned(struct ext4_allocation_context *ac,
        BUG_ON(sbi->s_stripe == 0);
 
        /* find first stripe-aligned block in group */
-       first_group_block = e4b->bd_group * EXT4_BLOCKS_PER_GROUP(sb)
-               + le32_to_cpu(sbi->s_es->s_first_data_block);
+       first_group_block = ext4_group_first_block_no(sb, e4b->bd_group);
+
        a = first_group_block + sbi->s_stripe - 1;
        do_div(a, sbi->s_stripe);
        i = (a * sbi->s_stripe) - first_group_block;
@@ -2256,7 +2253,7 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
 
        INIT_LIST_HEAD(&meta_group_info[i]->bb_prealloc_list);
        init_rwsem(&meta_group_info[i]->alloc_sem);
-       meta_group_info[i]->bb_free_root.rb_node = NULL;
+       meta_group_info[i]->bb_free_root = RB_ROOT;
 
 #ifdef DOUBLE_CHECK
        {
@@ -2560,12 +2557,9 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
                ext4_unlock_group(sb, entry->group);
                if (test_opt(sb, DISCARD)) {
                        ext4_fsblk_t discard_block;
-                       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
 
-                       discard_block = (ext4_fsblk_t)entry->group *
-                                               EXT4_BLOCKS_PER_GROUP(sb)
-                                       + entry->start_blk
-                                       + le32_to_cpu(es->s_first_data_block);
+                       discard_block = entry->start_blk +
+                               ext4_group_first_block_no(sb, entry->group);
                        trace_ext4_discard_blocks(sb,
                                        (unsigned long long)discard_block,
                                        entry->count);
@@ -2703,14 +2697,11 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
        if (err)
                goto out_err;
 
-       block = ac->ac_b_ex.fe_group * EXT4_BLOCKS_PER_GROUP(sb)
-               + ac->ac_b_ex.fe_start
-               + le32_to_cpu(es->s_first_data_block);
+       block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex);
 
        len = ac->ac_b_ex.fe_len;
        if (!ext4_data_block_valid(sbi, block, len)) {
-               ext4_error(sb, __func__,
-                          "Allocating blocks %llu-%llu which overlap "
+               ext4_error(sb, "Allocating blocks %llu-%llu which overlap "
                           "fs metadata\n", block, block+len);
                /* File system mounted not to panic on error
                 * Fix the bitmap and repeat the block allocation
@@ -3161,9 +3152,7 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac)
                /* The max size of hash table is PREALLOC_TB_SIZE */
                order = PREALLOC_TB_SIZE - 1;
 
-       goal_block = ac->ac_g_ex.fe_group * EXT4_BLOCKS_PER_GROUP(ac->ac_sb) +
-                    ac->ac_g_ex.fe_start +
-                    le32_to_cpu(EXT4_SB(ac->ac_sb)->s_es->s_first_data_block);
+       goal_block = ext4_grp_offs_to_block(ac->ac_sb, &ac->ac_g_ex);
        /*
         * search for the prealloc space that is having
         * minimal distance from the goal block.
@@ -3526,8 +3515,7 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh,
                if (bit >= end)
                        break;
                next = mb_find_next_bit(bitmap_bh->b_data, end, bit);
-               start = group * EXT4_BLOCKS_PER_GROUP(sb) + bit +
-                               le32_to_cpu(sbi->s_es->s_first_data_block);
+               start = ext4_group_first_block_no(sb, group) + bit;
                mb_debug(1, "    free preallocated %u/%u in group %u\n",
                                (unsigned) start, (unsigned) next - bit,
                                (unsigned) group);
@@ -3623,15 +3611,13 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
 
        bitmap_bh = ext4_read_block_bitmap(sb, group);
        if (bitmap_bh == NULL) {
-               ext4_error(sb, __func__, "Error in reading block "
-                               "bitmap for %u", group);
+               ext4_error(sb, "Error reading block bitmap for %u", group);
                return 0;
        }
 
        err = ext4_mb_load_buddy(sb, group, &e4b);
        if (err) {
-               ext4_error(sb, __func__, "Error in loading buddy "
-                               "information for %u", group);
+               ext4_error(sb, "Error loading buddy information for %u", group);
                put_bh(bitmap_bh);
                return 0;
        }
@@ -3804,15 +3790,15 @@ repeat:
 
                err = ext4_mb_load_buddy(sb, group, &e4b);
                if (err) {
-                       ext4_error(sb, __func__, "Error in loading buddy "
-                                       "information for %u", group);
+                       ext4_error(sb, "Error loading buddy information for %u",
+                                       group);
                        continue;
                }
 
                bitmap_bh = ext4_read_block_bitmap(sb, group);
                if (bitmap_bh == NULL) {
-                       ext4_error(sb, __func__, "Error in reading block "
-                                       "bitmap for %u", group);
+                       ext4_error(sb, "Error reading block bitmap for %u",
+                                       group);
                        ext4_mb_release_desc(&e4b);
                        continue;
                }
@@ -3938,7 +3924,7 @@ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac)
 
        /* don't use group allocation for large files */
        size = max(size, isize);
-       if (size >= sbi->s_mb_stream_request) {
+       if (size > sbi->s_mb_stream_request) {
                ac->ac_flags |= EXT4_MB_STREAM_ALLOC;
                return;
        }
@@ -4077,8 +4063,8 @@ ext4_mb_discard_lg_preallocations(struct super_block *sb,
 
                ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, NULL);
                if (ext4_mb_load_buddy(sb, group, &e4b)) {
-                       ext4_error(sb, __func__, "Error in loading buddy "
-                                       "information for %u", group);
+                       ext4_error(sb, "Error loading buddy information for %u",
+                                       group);
                        continue;
                }
                ext4_lock_group(sb, group);
@@ -4254,7 +4240,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
                        return 0;
                }
                reserv_blks = ar->len;
-               while (ar->len && vfs_dq_alloc_block(ar->inode, ar->len)) {
+               while (ar->len && dquot_alloc_block(ar->inode, ar->len)) {
                        ar->flags |= EXT4_MB_HINT_NOPREALLOC;
                        ar->len--;
                }
@@ -4331,7 +4317,7 @@ out2:
        kmem_cache_free(ext4_ac_cachep, ac);
 out1:
        if (inquota && ar->len < inquota)
-               vfs_dq_free_block(ar->inode, inquota - ar->len);
+               dquot_free_block(ar->inode, inquota - ar->len);
 out3:
        if (!ar->len) {
                if (!EXT4_I(ar->inode)->i_delalloc_reserved_flag)
@@ -4476,10 +4462,10 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
 
        sbi = EXT4_SB(sb);
        es = EXT4_SB(sb)->s_es;
-       if (!ext4_data_block_valid(sbi, block, count)) {
-               ext4_error(sb, __func__,
-                           "Freeing blocks not in datazone - "
-                           "block = %llu, count = %lu", block, count);
+       if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) &&
+           !ext4_data_block_valid(sbi, block, count)) {
+               ext4_error(sb, "Freeing blocks not in datazone - "
+                          "block = %llu, count = %lu", block, count);
                goto error_return;
        }
 
@@ -4547,8 +4533,7 @@ do_more:
            in_range(block + count - 1, ext4_inode_table(sb, gdp),
                      EXT4_SB(sb)->s_itb_per_group)) {
 
-               ext4_error(sb, __func__,
-                          "Freeing blocks in system zone - "
+               ext4_error(sb, "Freeing blocks in system zone - "
                           "Block = %llu, count = %lu", block, count);
                /* err = 0. ext4_std_error should be a no op */
                goto error_return;
@@ -4646,7 +4631,7 @@ do_more:
        sb->s_dirt = 1;
 error_return:
        if (freed)
-               vfs_dq_free_block(inode, freed);
+               dquot_free_block(inode, freed);
        brelse(bitmap_bh);
        ext4_std_error(sb, err);
        if (ac)
index 436521cae45662629d02871d5ff8429aee92065b..b619322c76f0c5ea56d831b787d77e2911657a25 100644 (file)
@@ -220,16 +220,9 @@ struct ext4_buddy {
 #define EXT4_MB_BITMAP(e4b)    ((e4b)->bd_bitmap)
 #define EXT4_MB_BUDDY(e4b)     ((e4b)->bd_buddy)
 
-#define in_range(b, first, len)        ((b) >= (first) && (b) <= (first) + (len) - 1)
-
 static inline ext4_fsblk_t ext4_grp_offs_to_block(struct super_block *sb,
                                        struct ext4_free_extent *fex)
 {
-       ext4_fsblk_t block;
-
-       block = (ext4_fsblk_t) fex->fe_group * EXT4_BLOCKS_PER_GROUP(sb)
-                       + fex->fe_start
-                       + le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
-       return block;
+       return ext4_group_first_block_no(sb, fex->fe_group) + fex->fe_start;
 }
 #endif
index 81415814b00b06a0455402078cd41588a615a701..8b87bd0eac954fc292b8644bebe758e03b0ca023 100644 (file)
@@ -365,12 +365,12 @@ static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode,
         * happened after we started the migrate. We need to
         * fail the migrate
         */
-       if (!(EXT4_I(inode)->i_state & EXT4_STATE_EXT_MIGRATE)) {
+       if (!ext4_test_inode_state(inode, EXT4_STATE_EXT_MIGRATE)) {
                retval = -EAGAIN;
                up_write(&EXT4_I(inode)->i_data_sem);
                goto err_out;
        } else
-               EXT4_I(inode)->i_state &= ~EXT4_STATE_EXT_MIGRATE;
+               ext4_clear_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
        /*
         * We have the extent map build with the tmp inode.
         * Now copy the i_data across
@@ -503,14 +503,10 @@ int ext4_ext_migrate(struct inode *inode)
        }
        i_size_write(tmp_inode, i_size_read(inode));
        /*
-        * We don't want the inode to be reclaimed
-        * if we got interrupted in between. We have
-        * this tmp inode carrying reference to the
-        * data blocks of the original file. We set
-        * the i_nlink to zero at the last stage after
-        * switching the original file to extent format
+        * Set the i_nlink to zero so it will be deleted later
+        * when we drop inode reference.
         */
-       tmp_inode->i_nlink = 1;
+       tmp_inode->i_nlink = 0;
 
        ext4_ext_tree_init(handle, tmp_inode);
        ext4_orphan_add(handle, tmp_inode);
@@ -533,10 +529,20 @@ int ext4_ext_migrate(struct inode *inode)
         * allocation.
         */
        down_read((&EXT4_I(inode)->i_data_sem));
-       EXT4_I(inode)->i_state |= EXT4_STATE_EXT_MIGRATE;
+       ext4_set_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
        up_read((&EXT4_I(inode)->i_data_sem));
 
        handle = ext4_journal_start(inode, 1);
+       if (IS_ERR(handle)) {
+               /*
+                * It is impossible to update on-disk structures without
+                * a handle, so just rollback in-core changes and live other
+                * work to orphan_list_cleanup()
+                */
+               ext4_orphan_del(NULL, tmp_inode);
+               retval = PTR_ERR(handle);
+               goto out;
+       }
 
        ei = EXT4_I(inode);
        i_data = ei->i_data;
@@ -618,15 +624,8 @@ err_out:
 
        /* Reset the extent details */
        ext4_ext_tree_init(handle, tmp_inode);
-
-       /*
-        * Set the i_nlink to zero so that
-        * generic_drop_inode really deletes the
-        * inode
-        */
-       tmp_inode->i_nlink = 0;
-
        ext4_journal_stop(handle);
+out:
        unlock_new_inode(tmp_inode);
        iput(tmp_inode);
 
index 82c415be87a46be61a36dc205d24c47955937e03..aa5fe28d180f2dedc3950a1e54b0b372462c8d32 100644 (file)
@@ -152,12 +152,12 @@ mext_check_null_inode(struct inode *inode1, struct inode *inode2,
        int ret = 0;
 
        if (inode1 == NULL) {
-               ext4_error(inode2->i_sb, function,
+               __ext4_error(inode2->i_sb, function,
                        "Both inodes should not be NULL: "
                        "inode1 NULL inode2 %lu", inode2->i_ino);
                ret = -EIO;
        } else if (inode2 == NULL) {
-               ext4_error(inode1->i_sb, function,
+               __ext4_error(inode1->i_sb, function,
                        "Both inodes should not be NULL: "
                        "inode1 %lu inode2 NULL", inode1->i_ino);
                ret = -EIO;
@@ -252,6 +252,7 @@ mext_insert_across_blocks(handle_t *handle, struct inode *orig_inode,
                }
 
                o_start->ee_len = start_ext->ee_len;
+               eblock = le32_to_cpu(start_ext->ee_block);
                new_flag = 1;
 
        } else if (start_ext->ee_len && new_ext->ee_len &&
@@ -262,6 +263,7 @@ mext_insert_across_blocks(handle_t *handle, struct inode *orig_inode,
                 * orig  |------------------------------|
                 */
                o_start->ee_len = start_ext->ee_len;
+               eblock = le32_to_cpu(start_ext->ee_block);
                new_flag = 1;
 
        } else if (!start_ext->ee_len && new_ext->ee_len &&
@@ -475,7 +477,6 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
        struct ext4_extent *oext, *o_start, *o_end, *prev_ext;
        struct ext4_extent new_ext, start_ext, end_ext;
        ext4_lblk_t new_ext_end;
-       ext4_fsblk_t new_phys_end;
        int oext_alen, new_ext_alen, end_ext_alen;
        int depth = ext_depth(orig_inode);
        int ret;
@@ -489,7 +490,6 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
        new_ext.ee_len = dext->ee_len;
        new_ext_alen = ext4_ext_get_actual_len(&new_ext);
        new_ext_end = le32_to_cpu(new_ext.ee_block) + new_ext_alen - 1;
-       new_phys_end = ext_pblock(&new_ext) + new_ext_alen - 1;
 
        /*
         * Case: original extent is first
@@ -502,6 +502,7 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
                le32_to_cpu(oext->ee_block) + oext_alen) {
                start_ext.ee_len = cpu_to_le16(le32_to_cpu(new_ext.ee_block) -
                                               le32_to_cpu(oext->ee_block));
+               start_ext.ee_block = oext->ee_block;
                copy_extent_status(oext, &start_ext);
        } else if (oext > EXT_FIRST_EXTENT(orig_path[depth].p_hdr)) {
                prev_ext = oext - 1;
@@ -515,6 +516,7 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
                        start_ext.ee_len = cpu_to_le16(
                                ext4_ext_get_actual_len(prev_ext) +
                                new_ext_alen);
+                       start_ext.ee_block = oext->ee_block;
                        copy_extent_status(prev_ext, &start_ext);
                        new_ext.ee_len = 0;
                }
@@ -526,7 +528,7 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
         * new_ext       |-------|
         */
        if (le32_to_cpu(oext->ee_block) + oext_alen - 1 < new_ext_end) {
-               ext4_error(orig_inode->i_sb, __func__,
+               ext4_error(orig_inode->i_sb,
                        "new_ext_end(%u) should be less than or equal to "
                        "oext->ee_block(%u) + oext_alen(%d) - 1",
                        new_ext_end, le32_to_cpu(oext->ee_block),
@@ -689,12 +691,12 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
        while (1) {
                /* The extent for donor must be found. */
                if (!dext) {
-                       ext4_error(donor_inode->i_sb, __func__,
+                       ext4_error(donor_inode->i_sb,
                                   "The extent for donor must be found");
                        *err = -EIO;
                        goto out;
                } else if (donor_off != le32_to_cpu(tmp_dext.ee_block)) {
-                       ext4_error(donor_inode->i_sb, __func__,
+                       ext4_error(donor_inode->i_sb,
                                "Donor offset(%u) and the first block of donor "
                                "extent(%u) should be equal",
                                donor_off,
@@ -928,7 +930,7 @@ out2:
 }
 
 /**
- * mext_check_argumants - Check whether move extent can be done
+ * mext_check_arguments - Check whether move extent can be done
  *
  * @orig_inode:                original inode
  * @donor_inode:       donor inode
@@ -949,14 +951,6 @@ mext_check_arguments(struct inode *orig_inode,
        unsigned int blkbits = orig_inode->i_blkbits;
        unsigned int blocksize = 1 << blkbits;
 
-       /* Regular file check */
-       if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
-               ext4_debug("ext4 move extent: The argument files should be "
-                       "regular file [ino:orig %lu, donor %lu]\n",
-                       orig_inode->i_ino, donor_inode->i_ino);
-               return -EINVAL;
-       }
-
        if (donor_inode->i_mode & (S_ISUID|S_ISGID)) {
                ext4_debug("ext4 move extent: suid or sgid is set"
                           " to donor file [ino:orig %lu, donor %lu]\n",
@@ -1204,6 +1198,14 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                return -EINVAL;
        }
 
+       /* Regular file check */
+       if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
+               ext4_debug("ext4 move extent: The argument files should be "
+                       "regular file [ino:orig %lu, donor %lu]\n",
+                       orig_inode->i_ino, donor_inode->i_ino);
+               return -EINVAL;
+       }
+
        /* Protect orig and donor inodes against a truncate */
        ret1 = mext_inode_double_lock(orig_inode, donor_inode);
        if (ret1 < 0)
@@ -1351,7 +1353,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                        if (ret1 < 0)
                                break;
                        if (*moved_len > len) {
-                               ext4_error(orig_inode->i_sb, __func__,
+                               ext4_error(orig_inode->i_sb,
                                        "We replaced blocks too much! "
                                        "sum of replaced: %llu requested: %llu",
                                        *moved_len, len);
index 17a17e10dd605198ddfe2dfed530d4cfff6812c1..0c070fabd10862beb71e17b51b96f99ae173a240 100644 (file)
@@ -383,8 +383,7 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
        if (root->info.hash_version != DX_HASH_TEA &&
            root->info.hash_version != DX_HASH_HALF_MD4 &&
            root->info.hash_version != DX_HASH_LEGACY) {
-               ext4_warning(dir->i_sb, __func__,
-                            "Unrecognised inode hash code %d",
+               ext4_warning(dir->i_sb, "Unrecognised inode hash code %d",
                             root->info.hash_version);
                brelse(bh);
                *err = ERR_BAD_DX_DIR;
@@ -399,8 +398,7 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
        hash = hinfo->hash;
 
        if (root->info.unused_flags & 1) {
-               ext4_warning(dir->i_sb, __func__,
-                            "Unimplemented inode hash flags: %#06x",
+               ext4_warning(dir->i_sb, "Unimplemented inode hash flags: %#06x",
                             root->info.unused_flags);
                brelse(bh);
                *err = ERR_BAD_DX_DIR;
@@ -408,8 +406,7 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
        }
 
        if ((indirect = root->info.indirect_levels) > 1) {
-               ext4_warning(dir->i_sb, __func__,
-                            "Unimplemented inode hash depth: %#06x",
+               ext4_warning(dir->i_sb, "Unimplemented inode hash depth: %#06x",
                             root->info.indirect_levels);
                brelse(bh);
                *err = ERR_BAD_DX_DIR;
@@ -421,8 +418,7 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
 
        if (dx_get_limit(entries) != dx_root_limit(dir,
                                                   root->info.info_length)) {
-               ext4_warning(dir->i_sb, __func__,
-                            "dx entry: limit != root limit");
+               ext4_warning(dir->i_sb, "dx entry: limit != root limit");
                brelse(bh);
                *err = ERR_BAD_DX_DIR;
                goto fail;
@@ -433,7 +429,7 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
        {
                count = dx_get_count(entries);
                if (!count || count > dx_get_limit(entries)) {
-                       ext4_warning(dir->i_sb, __func__,
+                       ext4_warning(dir->i_sb,
                                     "dx entry: no count or count > limit");
                        brelse(bh);
                        *err = ERR_BAD_DX_DIR;
@@ -478,7 +474,7 @@ dx_probe(const struct qstr *d_name, struct inode *dir,
                        goto fail2;
                at = entries = ((struct dx_node *) bh->b_data)->entries;
                if (dx_get_limit(entries) != dx_node_limit (dir)) {
-                       ext4_warning(dir->i_sb, __func__,
+                       ext4_warning(dir->i_sb,
                                     "dx entry: limit != node limit");
                        brelse(bh);
                        *err = ERR_BAD_DX_DIR;
@@ -494,7 +490,7 @@ fail2:
        }
 fail:
        if (*err == ERR_BAD_DX_DIR)
-               ext4_warning(dir->i_sb, __func__,
+               ext4_warning(dir->i_sb,
                             "Corrupt dir inode %ld, running e2fsck is "
                             "recommended.", dir->i_ino);
        return NULL;
@@ -947,9 +943,8 @@ restart:
                wait_on_buffer(bh);
                if (!buffer_uptodate(bh)) {
                        /* read error, skip block & hope for the best */
-                       ext4_error(sb, __func__, "reading directory #%lu "
-                                  "offset %lu", dir->i_ino,
-                                  (unsigned long)block);
+                       ext4_error(sb, "reading directory #%lu offset %lu",
+                                  dir->i_ino, (unsigned long)block);
                        brelse(bh);
                        goto next;
                }
@@ -1041,7 +1036,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
                retval = ext4_htree_next_block(dir, hash, frame,
                                               frames, NULL);
                if (retval < 0) {
-                       ext4_warning(sb, __func__,
+                       ext4_warning(sb,
                             "error reading index page in directory #%lu",
                             dir->i_ino);
                        *err = retval;
@@ -1071,14 +1066,13 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru
                __u32 ino = le32_to_cpu(de->inode);
                brelse(bh);
                if (!ext4_valid_inum(dir->i_sb, ino)) {
-                       ext4_error(dir->i_sb, "ext4_lookup",
-                                  "bad inode number: %u", ino);
+                       ext4_error(dir->i_sb, "bad inode number: %u", ino);
                        return ERR_PTR(-EIO);
                }
                inode = ext4_iget(dir->i_sb, ino);
                if (unlikely(IS_ERR(inode))) {
                        if (PTR_ERR(inode) == -ESTALE) {
-                               ext4_error(dir->i_sb, __func__,
+                               ext4_error(dir->i_sb,
                                                "deleted inode referenced: %u",
                                                ino);
                                return ERR_PTR(-EIO);
@@ -1110,7 +1104,7 @@ struct dentry *ext4_get_parent(struct dentry *child)
        brelse(bh);
 
        if (!ext4_valid_inum(child->d_inode->i_sb, ino)) {
-               ext4_error(child->d_inode->i_sb, "ext4_get_parent",
+               ext4_error(child->d_inode->i_sb,
                           "bad inode number: %u", ino);
                return ERR_PTR(-EIO);
        }
@@ -1410,7 +1404,7 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        de = (struct ext4_dir_entry_2 *)((char *)fde +
                ext4_rec_len_from_disk(fde->rec_len, blocksize));
        if ((char *) de >= (((char *) root) + blocksize)) {
-               ext4_error(dir->i_sb, __func__,
+               ext4_error(dir->i_sb,
                           "invalid rec_len for '..' in inode %lu",
                           dir->i_ino);
                brelse(bh);
@@ -1575,8 +1569,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
 
                if (levels && (dx_get_count(frames->entries) ==
                               dx_get_limit(frames->entries))) {
-                       ext4_warning(sb, __func__,
-                                    "Directory index full!");
+                       ext4_warning(sb, "Directory index full!");
                        err = -ENOSPC;
                        goto cleanup;
                }
@@ -1766,6 +1759,8 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, int mode,
        struct inode *inode;
        int err, retries = 0;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -1800,6 +1795,8 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -1837,6 +1834,8 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (EXT4_DIR_LINK_MAX(dir))
                return -EMLINK;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
@@ -1916,11 +1915,11 @@ static int empty_dir(struct inode *inode)
        if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) ||
            !(bh = ext4_bread(NULL, inode, 0, 0, &err))) {
                if (err)
-                       ext4_error(inode->i_sb, __func__,
+                       ext4_error(inode->i_sb,
                                   "error %d reading directory #%lu offset 0",
                                   err, inode->i_ino);
                else
-                       ext4_warning(inode->i_sb, __func__,
+                       ext4_warning(inode->i_sb,
                                     "bad directory (dir #%lu) - no data block",
                                     inode->i_ino);
                return 1;
@@ -1931,7 +1930,7 @@ static int empty_dir(struct inode *inode)
                        !le32_to_cpu(de1->inode) ||
                        strcmp(".", de->name) ||
                        strcmp("..", de1->name)) {
-               ext4_warning(inode->i_sb, "empty_dir",
+               ext4_warning(inode->i_sb,
                             "bad directory (dir #%lu) - no `.' or `..'",
                             inode->i_ino);
                brelse(bh);
@@ -1949,7 +1948,7 @@ static int empty_dir(struct inode *inode)
                                offset >> EXT4_BLOCK_SIZE_BITS(sb), 0, &err);
                        if (!bh) {
                                if (err)
-                                       ext4_error(sb, __func__,
+                                       ext4_error(sb,
                                                   "error %d reading directory"
                                                   " #%lu offset %u",
                                                   err, inode->i_ino, offset);
@@ -2020,11 +2019,18 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
        err = ext4_reserve_inode_write(handle, inode, &iloc);
        if (err)
                goto out_unlock;
+       /*
+        * Due to previous errors inode may be already a part of on-disk
+        * orphan list. If so skip on-disk list modification.
+        */
+       if (NEXT_ORPHAN(inode) && NEXT_ORPHAN(inode) <=
+               (le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count)))
+                       goto mem_insert;
 
        /* 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, inode, EXT4_SB(sb)->s_sbh);
+       err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
        rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
        if (!err)
                err = rc;
@@ -2037,6 +2043,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
         *
         * This is safe: on error we're going to ignore the orphan list
         * anyway on the next recovery. */
+mem_insert:
        if (!err)
                list_add(&EXT4_I(inode)->i_orphan, &EXT4_SB(sb)->s_orphan);
 
@@ -2096,7 +2103,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, inode, sbi->s_sbh);
+               err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
        } else {
                struct ext4_iloc iloc2;
                struct inode *i_prev =
@@ -2136,7 +2143,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
 
        /* Initialize quotas before so that eventual writes go in
         * separate transaction */
-       vfs_dq_init(dentry->d_inode);
+       dquot_initialize(dir);
+       dquot_initialize(dentry->d_inode);
+
        handle = ext4_journal_start(dir, EXT4_DELETE_TRANS_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
@@ -2163,7 +2172,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
        if (retval)
                goto end_rmdir;
        if (!EXT4_DIR_LINK_EMPTY(inode))
-               ext4_warning(inode->i_sb, "ext4_rmdir",
+               ext4_warning(inode->i_sb,
                             "empty directory has too many links (%d)",
                             inode->i_nlink);
        inode->i_version++;
@@ -2195,7 +2204,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
 
        /* Initialize quotas before so that eventual writes go
         * in separate transaction */
-       vfs_dq_init(dentry->d_inode);
+       dquot_initialize(dir);
+       dquot_initialize(dentry->d_inode);
+
        handle = ext4_journal_start(dir, EXT4_DELETE_TRANS_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
@@ -2215,7 +2226,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
                goto end_unlink;
 
        if (!inode->i_nlink) {
-               ext4_warning(inode->i_sb, "ext4_unlink",
+               ext4_warning(inode->i_sb,
                             "Deleting nonexistent file (%lu), %d",
                             inode->i_ino, inode->i_nlink);
                inode->i_nlink = 1;
@@ -2250,6 +2261,8 @@ static int ext4_symlink(struct inode *dir,
        if (l > dir->i_sb->s_blocksize)
                return -ENAMETOOLONG;
 
+       dquot_initialize(dir);
+
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 +
@@ -2308,6 +2321,8 @@ static int ext4_link(struct dentry *old_dentry,
        if (inode->i_nlink >= EXT4_LINK_MAX)
                return -EMLINK;
 
+       dquot_initialize(dir);
+
        /*
         * Return -ENOENT if we've raced with unlink and i_nlink is 0.  Doing
         * otherwise has the potential to corrupt the orphan inode list.
@@ -2358,12 +2373,15 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct ext4_dir_entry_2 *old_de, *new_de;
        int retval, force_da_alloc = 0;
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        old_bh = new_bh = dir_bh = NULL;
 
        /* Initialize quotas before so that eventual writes go
         * in separate transaction */
        if (new_dentry->d_inode)
-               vfs_dq_init(new_dentry->d_inode);
+               dquot_initialize(new_dentry->d_inode);
        handle = ext4_journal_start(old_dir, 2 *
                                        EXT4_DATA_TRANS_BLOCKS(old_dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2);
@@ -2462,7 +2480,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                }
        }
        if (retval) {
-               ext4_warning(old_dir->i_sb, "ext4_rename",
+               ext4_warning(old_dir->i_sb,
                                "Deleting old file (%lu), %d, error=%d",
                                old_dir->i_ino, old_dir->i_nlink, retval);
        }
index 3b2c5541d8a686fe7c780a512c0acd3e6771b3ab..5692c48754a0a278b384f40da3f5d7891fea9ff3 100644 (file)
@@ -48,65 +48,54 @@ static int verify_group_input(struct super_block *sb,
 
        ext4_get_group_no_and_offset(sb, start, NULL, &offset);
        if (group != sbi->s_groups_count)
-               ext4_warning(sb, __func__,
-                            "Cannot add at group %u (only %u groups)",
+               ext4_warning(sb, "Cannot add at group %u (only %u groups)",
                             input->group, sbi->s_groups_count);
        else if (offset != 0)
-                       ext4_warning(sb, __func__, "Last group not full");
+                       ext4_warning(sb, "Last group not full");
        else if (input->reserved_blocks > input->blocks_count / 5)
-               ext4_warning(sb, __func__, "Reserved blocks too high (%u)",
+               ext4_warning(sb, "Reserved blocks too high (%u)",
                             input->reserved_blocks);
        else if (free_blocks_count < 0)
-               ext4_warning(sb, __func__, "Bad blocks count %u",
+               ext4_warning(sb, "Bad blocks count %u",
                             input->blocks_count);
        else if (!(bh = sb_bread(sb, end - 1)))
-               ext4_warning(sb, __func__,
-                            "Cannot read last block (%llu)",
+               ext4_warning(sb, "Cannot read last block (%llu)",
                             end - 1);
        else if (outside(input->block_bitmap, start, end))
-               ext4_warning(sb, __func__,
-                            "Block bitmap not in group (block %llu)",
+               ext4_warning(sb, "Block bitmap not in group (block %llu)",
                             (unsigned long long)input->block_bitmap);
        else if (outside(input->inode_bitmap, start, end))
-               ext4_warning(sb, __func__,
-                            "Inode bitmap not in group (block %llu)",
+               ext4_warning(sb, "Inode bitmap not in group (block %llu)",
                             (unsigned long long)input->inode_bitmap);
        else if (outside(input->inode_table, start, end) ||
                 outside(itend - 1, start, end))
-               ext4_warning(sb, __func__,
-                            "Inode table not in group (blocks %llu-%llu)",
+               ext4_warning(sb, "Inode table not in group (blocks %llu-%llu)",
                             (unsigned long long)input->inode_table, itend - 1);
        else if (input->inode_bitmap == input->block_bitmap)
-               ext4_warning(sb, __func__,
-                            "Block bitmap same as inode bitmap (%llu)",
+               ext4_warning(sb, "Block bitmap same as inode bitmap (%llu)",
                             (unsigned long long)input->block_bitmap);
        else if (inside(input->block_bitmap, input->inode_table, itend))
-               ext4_warning(sb, __func__,
-                            "Block bitmap (%llu) in inode table (%llu-%llu)",
+               ext4_warning(sb, "Block bitmap (%llu) in inode table "
+                            "(%llu-%llu)",
                             (unsigned long long)input->block_bitmap,
                             (unsigned long long)input->inode_table, itend - 1);
        else if (inside(input->inode_bitmap, input->inode_table, itend))
-               ext4_warning(sb, __func__,
-                            "Inode bitmap (%llu) in inode table (%llu-%llu)",
+               ext4_warning(sb, "Inode bitmap (%llu) in inode table "
+                            "(%llu-%llu)",
                             (unsigned long long)input->inode_bitmap,
                             (unsigned long long)input->inode_table, itend - 1);
        else if (inside(input->block_bitmap, start, metaend))
-               ext4_warning(sb, __func__,
-                            "Block bitmap (%llu) in GDT table"
-                            " (%llu-%llu)",
+               ext4_warning(sb, "Block bitmap (%llu) in GDT table (%llu-%llu)",
                             (unsigned long long)input->block_bitmap,
                             start, metaend - 1);
        else if (inside(input->inode_bitmap, start, metaend))
-               ext4_warning(sb, __func__,
-                            "Inode bitmap (%llu) in GDT table"
-                            " (%llu-%llu)",
+               ext4_warning(sb, "Inode bitmap (%llu) in GDT table (%llu-%llu)",
                             (unsigned long long)input->inode_bitmap,
                             start, metaend - 1);
        else if (inside(input->inode_table, start, metaend) ||
                 inside(itend - 1, start, metaend))
-               ext4_warning(sb, __func__,
-                            "Inode table (%llu-%llu) overlaps"
-                            "GDT table (%llu-%llu)",
+               ext4_warning(sb, "Inode table (%llu-%llu) overlaps GDT table "
+                            "(%llu-%llu)",
                             (unsigned long long)input->inode_table,
                             itend - 1, start, metaend - 1);
        else
@@ -364,8 +353,7 @@ static int verify_reserved_gdb(struct super_block *sb,
        while ((grp = ext4_list_backups(sb, &three, &five, &seven)) < end) {
                if (le32_to_cpu(*p++) !=
                    grp * EXT4_BLOCKS_PER_GROUP(sb) + blk){
-                       ext4_warning(sb, __func__,
-                                    "reserved GDT %llu"
+                       ext4_warning(sb, "reserved GDT %llu"
                                     " missing grp %d (%llu)",
                                     blk, grp,
                                     grp *
@@ -420,8 +408,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
          */
        if (EXT4_SB(sb)->s_sbh->b_blocknr !=
            le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) {
-               ext4_warning(sb, __func__,
-                       "won't resize using backup superblock at %llu",
+               ext4_warning(sb, "won't resize using backup superblock at %llu",
                        (unsigned long long)EXT4_SB(sb)->s_sbh->b_blocknr);
                return -EPERM;
        }
@@ -444,8 +431,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
 
        data = (__le32 *)dind->b_data;
        if (le32_to_cpu(data[gdb_num % EXT4_ADDR_PER_BLOCK(sb)]) != gdblock) {
-               ext4_warning(sb, __func__,
-                            "new group %u GDT block %llu not reserved",
+               ext4_warning(sb, "new group %u GDT block %llu not reserved",
                             input->group, gdblock);
                err = -EINVAL;
                goto exit_dind;
@@ -468,7 +454,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
                        GFP_NOFS);
        if (!n_group_desc) {
                err = -ENOMEM;
-               ext4_warning(sb, __func__,
+               ext4_warning(sb,
                              "not enough memory for %lu groups", gdb_num + 1);
                goto exit_inode;
        }
@@ -567,8 +553,7 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
        /* Get each reserved primary GDT block and verify it holds backups */
        for (res = 0; res < reserved_gdb; res++, blk++) {
                if (le32_to_cpu(*data) != blk) {
-                       ext4_warning(sb, __func__,
-                                    "reserved block %llu"
+                       ext4_warning(sb, "reserved block %llu"
                                     " not at offset %ld",
                                     blk,
                                     (long)(data - (__le32 *)dind->b_data));
@@ -713,8 +698,7 @@ static void update_backups(struct super_block *sb,
         */
 exit_err:
        if (err) {
-               ext4_warning(sb, __func__,
-                            "can't update backup for group %u (err %d), "
+               ext4_warning(sb, "can't update backup for group %u (err %d), "
                             "forcing fsck on next reboot", group, err);
                sbi->s_mount_state &= ~EXT4_VALID_FS;
                sbi->s_es->s_state &= cpu_to_le16(~EXT4_VALID_FS);
@@ -753,20 +737,19 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
 
        if (gdb_off == 0 && !EXT4_HAS_RO_COMPAT_FEATURE(sb,
                                        EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
-               ext4_warning(sb, __func__,
-                            "Can't resize non-sparse filesystem further");
+               ext4_warning(sb, "Can't resize non-sparse filesystem further");
                return -EPERM;
        }
 
        if (ext4_blocks_count(es) + input->blocks_count <
            ext4_blocks_count(es)) {
-               ext4_warning(sb, __func__, "blocks_count overflow");
+               ext4_warning(sb, "blocks_count overflow");
                return -EINVAL;
        }
 
        if (le32_to_cpu(es->s_inodes_count) + EXT4_INODES_PER_GROUP(sb) <
            le32_to_cpu(es->s_inodes_count)) {
-               ext4_warning(sb, __func__, "inodes_count overflow");
+               ext4_warning(sb, "inodes_count overflow");
                return -EINVAL;
        }
 
@@ -774,14 +757,13 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
                if (!EXT4_HAS_COMPAT_FEATURE(sb,
                                             EXT4_FEATURE_COMPAT_RESIZE_INODE)
                    || !le16_to_cpu(es->s_reserved_gdt_blocks)) {
-                       ext4_warning(sb, __func__,
+                       ext4_warning(sb,
                                     "No reserved GDT blocks, can't resize");
                        return -EPERM;
                }
                inode = ext4_iget(sb, EXT4_RESIZE_INO);
                if (IS_ERR(inode)) {
-                       ext4_warning(sb, __func__,
-                                    "Error opening resize inode");
+                       ext4_warning(sb, "Error opening resize inode");
                        return PTR_ERR(inode);
                }
        }
@@ -810,8 +792,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
 
        mutex_lock(&sbi->s_resize_lock);
        if (input->group != sbi->s_groups_count) {
-               ext4_warning(sb, __func__,
-                            "multiple resizers run on filesystem!");
+               ext4_warning(sb, "multiple resizers run on filesystem!");
                err = -EBUSY;
                goto exit_journal;
        }
@@ -997,13 +978,12 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
                        " too large to resize to %llu blocks safely\n",
                        sb->s_id, n_blocks_count);
                if (sizeof(sector_t) < 8)
-                       ext4_warning(sb, __func__, "CONFIG_LBDAF not enabled");
+                       ext4_warning(sb, "CONFIG_LBDAF not enabled");
                return -EINVAL;
        }
 
        if (n_blocks_count < o_blocks_count) {
-               ext4_warning(sb, __func__,
-                            "can't shrink FS - resize aborted");
+               ext4_warning(sb, "can't shrink FS - resize aborted");
                return -EBUSY;
        }
 
@@ -1011,15 +991,14 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
        ext4_get_group_no_and_offset(sb, o_blocks_count, &group, &last);
 
        if (last == 0) {
-               ext4_warning(sb, __func__,
-                            "need to use ext2online to resize further");
+               ext4_warning(sb, "need to use ext2online to resize further");
                return -EPERM;
        }
 
        add = EXT4_BLOCKS_PER_GROUP(sb) - last;
 
        if (o_blocks_count + add < o_blocks_count) {
-               ext4_warning(sb, __func__, "blocks_count overflow");
+               ext4_warning(sb, "blocks_count overflow");
                return -EINVAL;
        }
 
@@ -1027,16 +1006,13 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
                add = n_blocks_count - o_blocks_count;
 
        if (o_blocks_count + add < n_blocks_count)
-               ext4_warning(sb, __func__,
-                            "will only finish group (%llu"
-                            " blocks, %u new)",
+               ext4_warning(sb, "will only finish group (%llu blocks, %u new)",
                             o_blocks_count + add, add);
 
        /* See if the device is actually as big as what was requested */
        bh = sb_bread(sb, o_blocks_count + add - 1);
        if (!bh) {
-               ext4_warning(sb, __func__,
-                            "can't read last block, resize aborted");
+               ext4_warning(sb, "can't read last block, resize aborted");
                return -ENOSPC;
        }
        brelse(bh);
@@ -1047,14 +1023,13 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
        handle = ext4_journal_start_sb(sb, 3);
        if (IS_ERR(handle)) {
                err = PTR_ERR(handle);
-               ext4_warning(sb, __func__, "error %d on journal start", err);
+               ext4_warning(sb, "error %d on journal start", err);
                goto exit_put;
        }
 
        mutex_lock(&EXT4_SB(sb)->s_resize_lock);
        if (o_blocks_count != ext4_blocks_count(es)) {
-               ext4_warning(sb, __func__,
-                            "multiple resizers run on filesystem!");
+               ext4_warning(sb, "multiple resizers run on filesystem!");
                mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
                ext4_journal_stop(handle);
                err = -EBUSY;
@@ -1063,8 +1038,7 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
 
        if ((err = ext4_journal_get_write_access(handle,
                                                 EXT4_SB(sb)->s_sbh))) {
-               ext4_warning(sb, __func__,
-                            "error %d on journal write access", err);
+               ext4_warning(sb, "error %d on journal write access", err);
                mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
                ext4_journal_stop(handle);
                goto exit_put;
index 735c20d5fd56565dcb53adbf2726acb9cc79fe8c..2b83b96cb2eb8e6bb01adc90e1a3f23a012049cf 100644 (file)
@@ -333,7 +333,7 @@ static void ext4_handle_error(struct super_block *sb)
                        sb->s_id);
 }
 
-void ext4_error(struct super_block *sb, const char *function,
+void __ext4_error(struct super_block *sb, const char *function,
                const char *fmt, ...)
 {
        va_list args;
@@ -347,6 +347,42 @@ void ext4_error(struct super_block *sb, const char *function,
        ext4_handle_error(sb);
 }
 
+void ext4_error_inode(const char *function, struct inode *inode,
+                     const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       printk(KERN_CRIT "EXT4-fs error (device %s): %s: inode #%lu: (comm %s) ",
+              inode->i_sb->s_id, function, inode->i_ino, current->comm);
+       vprintk(fmt, args);
+       printk("\n");
+       va_end(args);
+
+       ext4_handle_error(inode->i_sb);
+}
+
+void ext4_error_file(const char *function, struct file *file,
+                    const char *fmt, ...)
+{
+       va_list args;
+       struct inode *inode = file->f_dentry->d_inode;
+       char pathname[80], *path;
+
+       va_start(args, fmt);
+       path = d_path(&(file->f_path), pathname, sizeof(pathname));
+       if (!path)
+               path = "(unknown)";
+       printk(KERN_CRIT
+              "EXT4-fs error (device %s): %s: inode #%lu (comm %s path %s): ",
+              inode->i_sb->s_id, function, inode->i_ino, current->comm, path);
+       vprintk(fmt, args);
+       printk("\n");
+       va_end(args);
+
+       ext4_handle_error(inode->i_sb);
+}
+
 static const char *ext4_decode_error(struct super_block *sb, int errno,
                                     char nbuf[16])
 {
@@ -450,7 +486,7 @@ void ext4_msg (struct super_block * sb, const char *prefix,
        va_end(args);
 }
 
-void ext4_warning(struct super_block *sb, const char *function,
+void __ext4_warning(struct super_block *sb, const char *function,
                  const char *fmt, ...)
 {
        va_list args;
@@ -507,7 +543,7 @@ void ext4_update_dynamic_rev(struct super_block *sb)
        if (le32_to_cpu(es->s_rev_level) > EXT4_GOOD_OLD_REV)
                return;
 
-       ext4_warning(sb, __func__,
+       ext4_warning(sb,
                     "updating to rev %d because of new feature flag, "
                     "running e2fsck is recommended",
                     EXT4_DYNAMIC_REV);
@@ -708,7 +744,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
 #ifdef CONFIG_QUOTA
        ei->i_reserved_quota = 0;
 #endif
-       INIT_LIST_HEAD(&ei->i_aio_dio_complete_list);
+       INIT_LIST_HEAD(&ei->i_completed_io_list);
+       spin_lock_init(&ei->i_completed_io_lock);
        ei->cur_aio_dio = NULL;
        ei->i_sync_tid = 0;
        ei->i_datasync_tid = 0;
@@ -761,6 +798,7 @@ static void destroy_inodecache(void)
 
 static void ext4_clear_inode(struct inode *inode)
 {
+       dquot_drop(inode);
        ext4_discard_preallocations(inode);
        if (EXT4_JOURNAL(inode))
                jbd2_journal_release_jbd_inode(EXT4_SB(inode->i_sb)->s_journal,
@@ -796,10 +834,10 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
        if (sbi->s_qf_names[GRPQUOTA])
                seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);
 
-       if (sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA)
+       if (test_opt(sb, USRQUOTA))
                seq_puts(seq, ",usrquota");
 
-       if (sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA)
+       if (test_opt(sb, GRPQUOTA))
                seq_puts(seq, ",grpquota");
 #endif
 }
@@ -926,6 +964,9 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
        if (test_opt(sb, NOLOAD))
                seq_puts(seq, ",norecovery");
 
+       if (test_opt(sb, DIOREAD_NOLOCK))
+               seq_puts(seq, ",dioread_nolock");
+
        ext4_show_quota_options(seq, sb);
 
        return 0;
@@ -1012,19 +1053,9 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
                                const char *data, size_t len, loff_t off);
 
 static const struct dquot_operations ext4_quota_operations = {
-       .initialize     = dquot_initialize,
-       .drop           = dquot_drop,
-       .alloc_space    = dquot_alloc_space,
-       .reserve_space  = dquot_reserve_space,
-       .claim_space    = dquot_claim_space,
-       .release_rsv    = dquot_release_reserved_space,
 #ifdef CONFIG_QUOTA
        .get_reserved_space = ext4_get_reserved_space,
 #endif
-       .alloc_inode    = dquot_alloc_inode,
-       .free_space     = dquot_free_space,
-       .free_inode     = dquot_free_inode,
-       .transfer       = dquot_transfer,
        .write_dquot    = ext4_write_dquot,
        .acquire_dquot  = ext4_acquire_dquot,
        .release_dquot  = ext4_release_dquot,
@@ -1109,6 +1140,7 @@ enum {
        Opt_stripe, Opt_delalloc, Opt_nodelalloc,
        Opt_block_validity, Opt_noblock_validity,
        Opt_inode_readahead_blks, Opt_journal_ioprio,
+       Opt_dioread_nolock, Opt_dioread_lock,
        Opt_discard, Opt_nodiscard,
 };
 
@@ -1176,6 +1208,8 @@ static const match_table_t tokens = {
        {Opt_auto_da_alloc, "auto_da_alloc=%u"},
        {Opt_auto_da_alloc, "auto_da_alloc"},
        {Opt_noauto_da_alloc, "noauto_da_alloc"},
+       {Opt_dioread_nolock, "dioread_nolock"},
+       {Opt_dioread_lock, "dioread_lock"},
        {Opt_discard, "discard"},
        {Opt_nodiscard, "nodiscard"},
        {Opt_err, NULL},
@@ -1205,6 +1239,66 @@ static ext4_fsblk_t get_sb_block(void **data)
 }
 
 #define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
+static char deprecated_msg[] = "Mount option \"%s\" will be removed by %s\n"
+       "Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
+
+#ifdef CONFIG_QUOTA
+static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       char *qname;
+
+       if (sb_any_quota_loaded(sb) &&
+               !sbi->s_qf_names[qtype]) {
+               ext4_msg(sb, KERN_ERR,
+                       "Cannot change journaled "
+                       "quota options when quota turned on");
+               return 0;
+       }
+       qname = match_strdup(args);
+       if (!qname) {
+               ext4_msg(sb, KERN_ERR,
+                       "Not enough memory for storing quotafile name");
+               return 0;
+       }
+       if (sbi->s_qf_names[qtype] &&
+               strcmp(sbi->s_qf_names[qtype], qname)) {
+               ext4_msg(sb, KERN_ERR,
+                       "%s quota file already specified", QTYPE2NAME(qtype));
+               kfree(qname);
+               return 0;
+       }
+       sbi->s_qf_names[qtype] = qname;
+       if (strchr(sbi->s_qf_names[qtype], '/')) {
+               ext4_msg(sb, KERN_ERR,
+                       "quotafile must be on filesystem root");
+               kfree(sbi->s_qf_names[qtype]);
+               sbi->s_qf_names[qtype] = NULL;
+               return 0;
+       }
+       set_opt(sbi->s_mount_opt, QUOTA);
+       return 1;
+}
+
+static int clear_qf_name(struct super_block *sb, int qtype)
+{
+
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+       if (sb_any_quota_loaded(sb) &&
+               sbi->s_qf_names[qtype]) {
+               ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options"
+                       " when quota turned on");
+               return 0;
+       }
+       /*
+        * The space will be released later when all options are confirmed
+        * to be correct
+        */
+       sbi->s_qf_names[qtype] = NULL;
+       return 1;
+}
+#endif
 
 static int parse_options(char *options, struct super_block *sb,
                         unsigned long *journal_devnum,
@@ -1217,8 +1311,7 @@ static int parse_options(char *options, struct super_block *sb,
        int data_opt = 0;
        int option;
 #ifdef CONFIG_QUOTA
-       int qtype, qfmt;
-       char *qname;
+       int qfmt;
 #endif
 
        if (!options)
@@ -1229,19 +1322,31 @@ static int parse_options(char *options, struct super_block *sb,
                if (!*p)
                        continue;
 
+               /*
+                * Initialize args struct so we know whether arg was
+                * found; some options take optional arguments.
+                */
+               args[0].to = args[0].from = 0;
                token = match_token(p, tokens, args);
                switch (token) {
                case Opt_bsd_df:
+                       ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
                        clear_opt(sbi->s_mount_opt, MINIX_DF);
                        break;
                case Opt_minix_df:
+                       ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
                        set_opt(sbi->s_mount_opt, MINIX_DF);
+
                        break;
                case Opt_grpid:
+                       ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
                        set_opt(sbi->s_mount_opt, GRPID);
+
                        break;
                case Opt_nogrpid:
+                       ext4_msg(sb, KERN_WARNING, deprecated_msg, p, "2.6.38");
                        clear_opt(sbi->s_mount_opt, GRPID);
+
                        break;
                case Opt_resuid:
                        if (match_int(&args[0], &option))
@@ -1378,14 +1483,13 @@ static int parse_options(char *options, struct super_block *sb,
                        data_opt = EXT4_MOUNT_WRITEBACK_DATA;
                datacheck:
                        if (is_remount) {
-                               if ((sbi->s_mount_opt & EXT4_MOUNT_DATA_FLAGS)
-                                               != data_opt) {
+                               if (test_opt(sb, DATA_FLAGS) != data_opt) {
                                        ext4_msg(sb, KERN_ERR,
                                                "Cannot change data mode on remount");
                                        return 0;
                                }
                        } else {
-                               sbi->s_mount_opt &= ~EXT4_MOUNT_DATA_FLAGS;
+                               clear_opt(sbi->s_mount_opt, DATA_FLAGS);
                                sbi->s_mount_opt |= data_opt;
                        }
                        break;
@@ -1397,63 +1501,22 @@ static int parse_options(char *options, struct super_block *sb,
                        break;
 #ifdef CONFIG_QUOTA
                case Opt_usrjquota:
-                       qtype = USRQUOTA;
-                       goto set_qf_name;
-               case Opt_grpjquota:
-                       qtype = GRPQUOTA;
-set_qf_name:
-                       if (sb_any_quota_loaded(sb) &&
-                           !sbi->s_qf_names[qtype]) {
-                               ext4_msg(sb, KERN_ERR,
-                                      "Cannot change journaled "
-                                      "quota options when quota turned on");
+                       if (!set_qf_name(sb, USRQUOTA, &args[0]))
                                return 0;
-                       }
-                       qname = match_strdup(&args[0]);
-                       if (!qname) {
-                               ext4_msg(sb, KERN_ERR,
-                                       "Not enough memory for "
-                                       "storing quotafile name");
-                               return 0;
-                       }
-                       if (sbi->s_qf_names[qtype] &&
-                           strcmp(sbi->s_qf_names[qtype], qname)) {
-                               ext4_msg(sb, KERN_ERR,
-                                       "%s quota file already "
-                                       "specified", QTYPE2NAME(qtype));
-                               kfree(qname);
-                               return 0;
-                       }
-                       sbi->s_qf_names[qtype] = qname;
-                       if (strchr(sbi->s_qf_names[qtype], '/')) {
-                               ext4_msg(sb, KERN_ERR,
-                                       "quotafile must be on "
-                                       "filesystem root");
-                               kfree(sbi->s_qf_names[qtype]);
-                               sbi->s_qf_names[qtype] = NULL;
+                       break;
+               case Opt_grpjquota:
+                       if (!set_qf_name(sb, GRPQUOTA, &args[0]))
                                return 0;
-                       }
-                       set_opt(sbi->s_mount_opt, QUOTA);
                        break;
                case Opt_offusrjquota:
-                       qtype = USRQUOTA;
-                       goto clear_qf_name;
+                       if (!clear_qf_name(sb, USRQUOTA))
+                               return 0;
+                       break;
                case Opt_offgrpjquota:
-                       qtype = GRPQUOTA;
-clear_qf_name:
-                       if (sb_any_quota_loaded(sb) &&
-                           sbi->s_qf_names[qtype]) {
-                               ext4_msg(sb, KERN_ERR, "Cannot change "
-                                       "journaled quota options when "
-                                       "quota turned on");
+                       if (!clear_qf_name(sb, GRPQUOTA))
                                return 0;
-                       }
-                       /*
-                        * The space will be released later when all options
-                        * are confirmed to be correct
-                        */
-                       sbi->s_qf_names[qtype] = NULL;
                        break;
+
                case Opt_jqfmt_vfsold:
                        qfmt = QFMT_VFS_OLD;
                        goto set_qf_format;
@@ -1518,10 +1581,11 @@ set_qf_format:
                        clear_opt(sbi->s_mount_opt, BARRIER);
                        break;
                case Opt_barrier:
-                       if (match_int(&args[0], &option)) {
-                               set_opt(sbi->s_mount_opt, BARRIER);
-                               break;
-                       }
+                       if (args[0].from) {
+                               if (match_int(&args[0], &option))
+                                       return 0;
+                       } else
+                               option = 1;     /* No argument, default to 1 */
                        if (option)
                                set_opt(sbi->s_mount_opt, BARRIER);
                        else
@@ -1594,10 +1658,11 @@ set_qf_format:
                        set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC);
                        break;
                case Opt_auto_da_alloc:
-                       if (match_int(&args[0], &option)) {
-                               clear_opt(sbi->s_mount_opt, NO_AUTO_DA_ALLOC);
-                               break;
-                       }
+                       if (args[0].from) {
+                               if (match_int(&args[0], &option))
+                                       return 0;
+                       } else
+                               option = 1;     /* No argument, default to 1 */
                        if (option)
                                clear_opt(sbi->s_mount_opt, NO_AUTO_DA_ALLOC);
                        else
@@ -1609,6 +1674,12 @@ set_qf_format:
                case Opt_nodiscard:
                        clear_opt(sbi->s_mount_opt, DISCARD);
                        break;
+               case Opt_dioread_nolock:
+                       set_opt(sbi->s_mount_opt, DIOREAD_NOLOCK);
+                       break;
+               case Opt_dioread_lock:
+                       clear_opt(sbi->s_mount_opt, DIOREAD_NOLOCK);
+                       break;
                default:
                        ext4_msg(sb, KERN_ERR,
                               "Unrecognized mount option \"%s\" "
@@ -1618,18 +1689,13 @@ set_qf_format:
        }
 #ifdef CONFIG_QUOTA
        if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
-               if ((sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA) &&
-                    sbi->s_qf_names[USRQUOTA])
+               if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
                        clear_opt(sbi->s_mount_opt, USRQUOTA);
 
-               if ((sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA) &&
-                    sbi->s_qf_names[GRPQUOTA])
+               if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
                        clear_opt(sbi->s_mount_opt, GRPQUOTA);
 
-               if ((sbi->s_qf_names[USRQUOTA] &&
-                               (sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA)) ||
-                   (sbi->s_qf_names[GRPQUOTA] &&
-                               (sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA))) {
+               if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
                        ext4_msg(sb, KERN_ERR, "old and new quota "
                                        "format mixing");
                        return 0;
@@ -1939,7 +2005,7 @@ static void ext4_orphan_cleanup(struct super_block *sb,
                }
 
                list_add(&EXT4_I(inode)->i_orphan, &EXT4_SB(sb)->s_orphan);
-               vfs_dq_init(inode);
+               dquot_initialize(inode);
                if (inode->i_nlink) {
                        ext4_msg(sb, KERN_DEBUG,
                                "%s: truncating inode %lu to %lld bytes",
@@ -2432,8 +2498,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
        if (def_mount_opts & EXT4_DEFM_DEBUG)
                set_opt(sbi->s_mount_opt, DEBUG);
-       if (def_mount_opts & EXT4_DEFM_BSDGROUPS)
+       if (def_mount_opts & EXT4_DEFM_BSDGROUPS) {
+               ext4_msg(sb, KERN_WARNING, deprecated_msg, "bsdgroups",
+                       "2.6.38");
                set_opt(sbi->s_mount_opt, GRPID);
+       }
        if (def_mount_opts & EXT4_DEFM_UID16)
                set_opt(sbi->s_mount_opt, NO_UID32);
 #ifdef CONFIG_EXT4_FS_XATTR
@@ -2445,11 +2514,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                set_opt(sbi->s_mount_opt, POSIX_ACL);
 #endif
        if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)
-               sbi->s_mount_opt |= EXT4_MOUNT_JOURNAL_DATA;
+               set_opt(sbi->s_mount_opt, JOURNAL_DATA);
        else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_ORDERED)
-               sbi->s_mount_opt |= EXT4_MOUNT_ORDERED_DATA;
+               set_opt(sbi->s_mount_opt, ORDERED_DATA);
        else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_WBACK)
-               sbi->s_mount_opt |= EXT4_MOUNT_WRITEBACK_DATA;
+               set_opt(sbi->s_mount_opt, WRITEBACK_DATA);
 
        if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_PANIC)
                set_opt(sbi->s_mount_opt, ERRORS_PANIC);
@@ -2477,7 +2546,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
 
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
-               ((sbi->s_mount_opt & EXT4_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+               (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
 
        if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&
            (EXT4_HAS_COMPAT_FEATURE(sb, ~0U) ||
@@ -2766,7 +2835,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
              EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) {
                ext4_msg(sb, KERN_ERR, "required journal recovery "
                       "suppressed and not mounted read-only");
-               goto failed_mount4;
+               goto failed_mount_wq;
        } else {
                clear_opt(sbi->s_mount_opt, DATA_FLAGS);
                set_opt(sbi->s_mount_opt, WRITEBACK_DATA);
@@ -2779,7 +2848,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
            !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_mount4;
+               goto failed_mount_wq;
        }
 
        if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
@@ -2818,7 +2887,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                    (sbi->s_journal, 0, 0, JBD2_FEATURE_INCOMPAT_REVOKE)) {
                        ext4_msg(sb, KERN_ERR, "Journal does not support "
                               "requested data journaling mode");
-                       goto failed_mount4;
+                       goto failed_mount_wq;
                }
        default:
                break;
@@ -2826,13 +2895,17 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        set_task_ioprio(sbi->s_journal->j_task, journal_ioprio);
 
 no_journal:
-
        if (test_opt(sb, NOBH)) {
                if (!(test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)) {
                        ext4_msg(sb, KERN_WARNING, "Ignoring nobh option - "
                                "its supported only with writeback mode");
                        clear_opt(sbi->s_mount_opt, NOBH);
                }
+               if (test_opt(sb, DIOREAD_NOLOCK)) {
+                       ext4_msg(sb, KERN_WARNING, "dioread_nolock option is "
+                               "not supported with nobh mode");
+                       goto failed_mount_wq;
+               }
        }
        EXT4_SB(sb)->dio_unwritten_wq = create_workqueue("ext4-dio-unwritten");
        if (!EXT4_SB(sb)->dio_unwritten_wq) {
@@ -2897,6 +2970,18 @@ no_journal:
                         "requested data journaling mode");
                clear_opt(sbi->s_mount_opt, DELALLOC);
        }
+       if (test_opt(sb, DIOREAD_NOLOCK)) {
+               if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
+                       ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock "
+                               "option - requested data journaling mode");
+                       clear_opt(sbi->s_mount_opt, DIOREAD_NOLOCK);
+               }
+               if (sb->s_blocksize < PAGE_SIZE) {
+                       ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock "
+                               "option - block size is too small");
+                       clear_opt(sbi->s_mount_opt, DIOREAD_NOLOCK);
+               }
+       }
 
        err = ext4_setup_system_zone(sb);
        if (err) {
@@ -3360,10 +3445,9 @@ static void ext4_clear_journal_err(struct super_block *sb,
                char nbuf[16];
 
                errstr = ext4_decode_error(sb, j_errno, nbuf);
-               ext4_warning(sb, __func__, "Filesystem error recorded "
+               ext4_warning(sb, "Filesystem error recorded "
                             "from previous mount: %s", errstr);
-               ext4_warning(sb, __func__, "Marking fs in need of "
-                            "filesystem check.");
+               ext4_warning(sb, "Marking fs in need of filesystem check.");
 
                EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
                es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
@@ -3514,7 +3598,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                ext4_abort(sb, __func__, "Abort forced by user");
 
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
-               ((sbi->s_mount_opt & EXT4_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+               (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
 
        es = sbi->s_es;
 
@@ -3708,7 +3792,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
  * Process 1                         Process 2
  * ext4_create()                     quota_sync()
  *   jbd2_journal_start()                  write_dquot()
- *   vfs_dq_init()                         down(dqio_mutex)
+ *   dquot_initialize()                         down(dqio_mutex)
  *     down(dqio_mutex)                    jbd2_journal_start()
  *
  */
@@ -3917,9 +4001,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
        ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb);
        int err = 0;
        int offset = off & (sb->s_blocksize - 1);
-       int tocopy;
        int journal_quota = EXT4_SB(sb)->s_qf_names[type] != NULL;
-       size_t towrite = len;
        struct buffer_head *bh;
        handle_t *handle = journal_current_handle();
 
@@ -3929,52 +4011,53 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
                        (unsigned long long)off, (unsigned long long)len);
                return -EIO;
        }
+       /*
+        * Since we account only one data block in transaction credits,
+        * then it is impossible to cross a block boundary.
+        */
+       if (sb->s_blocksize - offset < len) {
+               ext4_msg(sb, KERN_WARNING, "Quota write (off=%llu, len=%llu)"
+                       " cancelled because not block aligned",
+                       (unsigned long long)off, (unsigned long long)len);
+               return -EIO;
+       }
+
        mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
-       while (towrite > 0) {
-               tocopy = sb->s_blocksize - offset < towrite ?
-                               sb->s_blocksize - offset : towrite;
-               bh = ext4_bread(handle, inode, blk, 1, &err);
-               if (!bh)
+       bh = ext4_bread(handle, inode, blk, 1, &err);
+       if (!bh)
+               goto out;
+       if (journal_quota) {
+               err = ext4_journal_get_write_access(handle, bh);
+               if (err) {
+                       brelse(bh);
                        goto out;
-               if (journal_quota) {
-                       err = ext4_journal_get_write_access(handle, bh);
-                       if (err) {
-                               brelse(bh);
-                               goto out;
-                       }
                }
-               lock_buffer(bh);
-               memcpy(bh->b_data+offset, data, tocopy);
-               flush_dcache_page(bh->b_page);
-               unlock_buffer(bh);
-               if (journal_quota)
-                       err = ext4_handle_dirty_metadata(handle, NULL, bh);
-               else {
-                       /* Always do at least ordered writes for quotas */
-                       err = ext4_jbd2_file_inode(handle, inode);
-                       mark_buffer_dirty(bh);
-               }
-               brelse(bh);
-               if (err)
-                       goto out;
-               offset = 0;
-               towrite -= tocopy;
-               data += tocopy;
-               blk++;
        }
+       lock_buffer(bh);
+       memcpy(bh->b_data+offset, data, len);
+       flush_dcache_page(bh->b_page);
+       unlock_buffer(bh);
+       if (journal_quota)
+               err = ext4_handle_dirty_metadata(handle, NULL, bh);
+       else {
+               /* Always do at least ordered writes for quotas */
+               err = ext4_jbd2_file_inode(handle, inode);
+               mark_buffer_dirty(bh);
+       }
+       brelse(bh);
 out:
-       if (len == towrite) {
+       if (err) {
                mutex_unlock(&inode->i_mutex);
                return err;
        }
-       if (inode->i_size < off+len-towrite) {
-               i_size_write(inode, off+len-towrite);
+       if (inode->i_size < off + len) {
+               i_size_write(inode, off + len);
                EXT4_I(inode)->i_disksize = inode->i_size;
        }
        inode->i_mtime = inode->i_ctime = CURRENT_TIME;
        ext4_mark_inode_dirty(handle, inode);
        mutex_unlock(&inode->i_mutex);
-       return len - towrite;
+       return len;
 }
 
 #endif
index f3a2f7ed45aabc13e3ea4a28fc9baa098131cbb4..b4c5aa8489d851ea3478c8e25106a0c87846b3f6 100644 (file)
@@ -227,7 +227,8 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
        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)) {
-bad_block:     ext4_error(inode->i_sb, __func__,
+bad_block:
+               ext4_error(inode->i_sb,
                           "inode %lu: bad block %llu", inode->i_ino,
                           EXT4_I(inode)->i_file_acl);
                error = -EIO;
@@ -267,7 +268,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
        void *end;
        int error;
 
-       if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR))
+       if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
                return -ENODATA;
        error = ext4_get_inode_loc(inode, &iloc);
        if (error)
@@ -371,7 +372,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size)
        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)) {
-               ext4_error(inode->i_sb, __func__,
+               ext4_error(inode->i_sb,
                           "inode %lu: bad block %llu", inode->i_ino,
                           EXT4_I(inode)->i_file_acl);
                error = -EIO;
@@ -396,7 +397,7 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
        void *end;
        int error;
 
-       if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR))
+       if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
                return 0;
        error = ext4_get_inode_loc(inode, &iloc);
        if (error)
@@ -494,7 +495,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
                error = ext4_handle_dirty_metadata(handle, inode, bh);
                if (IS_SYNC(inode))
                        ext4_handle_sync(handle);
-               vfs_dq_free_block(inode, 1);
+               dquot_free_block(inode, 1);
                ea_bdebug(bh, "refcount now=%d; releasing",
                          le32_to_cpu(BHDR(bh)->h_refcount));
                if (ce)
@@ -665,9 +666,8 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i,
                        atomic_read(&(bs->bh->b_count)),
                        le32_to_cpu(BHDR(bs->bh)->h_refcount));
                if (ext4_xattr_check_block(bs->bh)) {
-                       ext4_error(sb, __func__,
-                               "inode %lu: bad block %llu", inode->i_ino,
-                               EXT4_I(inode)->i_file_acl);
+                       ext4_error(sb, "inode %lu: bad block %llu",
+                                  inode->i_ino, EXT4_I(inode)->i_file_acl);
                        error = -EIO;
                        goto cleanup;
                }
@@ -787,8 +787,8 @@ inserted:
                        else {
                                /* The old block is released after updating
                                   the inode. */
-                               error = -EDQUOT;
-                               if (vfs_dq_alloc_block(inode, 1))
+                               error = dquot_alloc_block(inode, 1);
+                               if (error)
                                        goto cleanup;
                                error = ext4_journal_get_write_access(handle,
                                                                      new_bh);
@@ -876,13 +876,12 @@ cleanup:
        return error;
 
 cleanup_dquot:
-       vfs_dq_free_block(inode, 1);
+       dquot_free_block(inode, 1);
        goto cleanup;
 
 bad_block:
-       ext4_error(inode->i_sb, __func__,
-                  "inode %lu: bad block %llu", inode->i_ino,
-                  EXT4_I(inode)->i_file_acl);
+       ext4_error(inode->i_sb, "inode %lu: bad block %llu",
+                  inode->i_ino, EXT4_I(inode)->i_file_acl);
        goto cleanup;
 
 #undef header
@@ -908,7 +907,7 @@ ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
        is->s.base = is->s.first = IFIRST(header);
        is->s.here = is->s.first;
        is->s.end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
-       if (EXT4_I(inode)->i_state & EXT4_STATE_XATTR) {
+       if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
                error = ext4_xattr_check_names(IFIRST(header), is->s.end);
                if (error)
                        return error;
@@ -940,10 +939,10 @@ ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
        header = IHDR(inode, ext4_raw_inode(&is->iloc));
        if (!IS_LAST_ENTRY(s->first)) {
                header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
-               EXT4_I(inode)->i_state |= EXT4_STATE_XATTR;
+               ext4_set_inode_state(inode, EXT4_STATE_XATTR);
        } else {
                header->h_magic = cpu_to_le32(0);
-               EXT4_I(inode)->i_state &= ~EXT4_STATE_XATTR;
+               ext4_clear_inode_state(inode, EXT4_STATE_XATTR);
        }
        return 0;
 }
@@ -986,8 +985,8 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
        if (strlen(name) > 255)
                return -ERANGE;
        down_write(&EXT4_I(inode)->xattr_sem);
-       no_expand = EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND;
-       EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND;
+       no_expand = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND);
+       ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
 
        error = ext4_get_inode_loc(inode, &is.iloc);
        if (error)
@@ -997,10 +996,10 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
        if (error)
                goto cleanup;
 
-       if (EXT4_I(inode)->i_state & EXT4_STATE_NEW) {
+       if (ext4_test_inode_state(inode, EXT4_STATE_NEW)) {
                struct ext4_inode *raw_inode = ext4_raw_inode(&is.iloc);
                memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size);
-               EXT4_I(inode)->i_state &= ~EXT4_STATE_NEW;
+               ext4_clear_inode_state(inode, EXT4_STATE_NEW);
        }
 
        error = ext4_xattr_ibody_find(inode, &i, &is);
@@ -1052,7 +1051,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
                ext4_xattr_update_super_block(handle, inode->i_sb);
                inode->i_ctime = ext4_current_time(inode);
                if (!value)
-                       EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND;
+                       ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
                error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
                /*
                 * The bh is consumed by ext4_mark_iloc_dirty, even with
@@ -1067,7 +1066,7 @@ cleanup:
        brelse(is.iloc.bh);
        brelse(bs.bh);
        if (no_expand == 0)
-               EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND;
+               ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
        up_write(&EXT4_I(inode)->xattr_sem);
        return error;
 }
@@ -1195,9 +1194,8 @@ retry:
                if (!bh)
                        goto cleanup;
                if (ext4_xattr_check_block(bh)) {
-                       ext4_error(inode->i_sb, __func__,
-                               "inode %lu: bad block %llu", inode->i_ino,
-                               EXT4_I(inode)->i_file_acl);
+                       ext4_error(inode->i_sb, "inode %lu: bad block %llu",
+                                  inode->i_ino, EXT4_I(inode)->i_file_acl);
                        error = -EIO;
                        goto cleanup;
                }
@@ -1302,6 +1300,8 @@ retry:
 
                /* Remove the chosen entry from the inode */
                error = ext4_xattr_ibody_set(handle, inode, &i, is);
+               if (error)
+                       goto cleanup;
 
                entry = IFIRST(header);
                if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize)
@@ -1372,16 +1372,14 @@ ext4_xattr_delete_inode(handle_t *handle, struct inode *inode)
                goto cleanup;
        bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);
        if (!bh) {
-               ext4_error(inode->i_sb, __func__,
-                       "inode %lu: block %llu read error", inode->i_ino,
-                       EXT4_I(inode)->i_file_acl);
+               ext4_error(inode->i_sb, "inode %lu: block %llu read error",
+                          inode->i_ino, EXT4_I(inode)->i_file_acl);
                goto cleanup;
        }
        if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
            BHDR(bh)->h_blocks != cpu_to_le32(1)) {
-               ext4_error(inode->i_sb, __func__,
-                       "inode %lu: bad block %llu", inode->i_ino,
-                       EXT4_I(inode)->i_file_acl);
+               ext4_error(inode->i_sb, "inode %lu: bad block %llu",
+                          inode->i_ino, EXT4_I(inode)->i_file_acl);
                goto cleanup;
        }
        ext4_xattr_release_block(handle, inode, bh);
@@ -1506,7 +1504,7 @@ again:
                }
                bh = sb_bread(inode->i_sb, ce->e_block);
                if (!bh) {
-                       ext4_error(inode->i_sb, __func__,
+                       ext4_error(inode->i_sb,
                                "inode %lu: block %lu read error",
                                inode->i_ino, (unsigned long) ce->e_block);
                } else if (le32_to_cpu(BHDR(bh)->h_refcount) >=
index 14da530b05cae63e189e3303dddc0caa2018638c..fbeecdc194dc70bbf9405a43b186e12cb8aa6f95 100644 (file)
@@ -577,7 +577,7 @@ static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
        return i_pos;
 }
 
-static int fat_write_inode(struct inode *inode, int wait)
+static int __fat_write_inode(struct inode *inode, int wait)
 {
        struct super_block *sb = inode->i_sb;
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -634,9 +634,14 @@ retry:
        return err;
 }
 
+static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       return __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
 int fat_sync_inode(struct inode *inode)
 {
-       return fat_write_inode(inode, 1);
+       return __fat_write_inode(inode, 1);
 }
 
 EXPORT_SYMBOL_GPL(fat_sync_inode);
index 97e01dc0d95fc4fe8464d729ce94d32b888b567d..452d02f9075ea19861d6d6bd00de12afd19a65eb 100644 (file)
@@ -344,7 +344,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        switch (cmd) {
        case F_DUPFD:
        case F_DUPFD_CLOEXEC:
-               if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+               if (arg >= rlimit(RLIMIT_NOFILE))
                        break;
                err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);
                if (err >= 0) {
index 38039af67663fd615ec80420e39d736687a5fa91..34bb7f71d99404e8b91a4bfb0654505d1e537318 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -257,7 +257,7 @@ int expand_files(struct files_struct *files, int nr)
         * N.B. For clone tasks sharing a files structure, this test
         * will limit the total number of files that can be opened.
         */
-       if (nr >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+       if (nr >= rlimit(RLIMIT_NOFILE))
                return -EMFILE;
 
        /* Do we need to expand? */
index b98404b5438385dc803498ba5e0c710a7244ed60..32d12b78bac8e2c05a80dc53386b355bdc10ef73 100644 (file)
@@ -393,7 +393,9 @@ retry:
                        continue;
                if (!(f->f_mode & FMODE_WRITE))
                        continue;
+               spin_lock(&f->f_lock);
                f->f_mode &= ~FMODE_WRITE;
+               spin_unlock(&f->f_lock);
                if (file_check_writeable(f) != 0)
                        continue;
                file_release_write(f);
index 1a7c42c64ff47ee7e55c378d0e067892d789e2d7..76fc4d594acb4a3c8091531df7ee37f974f192b6 100644 (file)
@@ -381,10 +381,10 @@ static void queue_io(struct bdi_writeback *wb, unsigned long *older_than_this)
        move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this);
 }
 
-static int write_inode(struct inode *inode, int sync)
+static int write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        if (inode->i_sb->s_op->write_inode && !is_bad_inode(inode))
-               return inode->i_sb->s_op->write_inode(inode, sync);
+               return inode->i_sb->s_op->write_inode(inode, wbc);
        return 0;
 }
 
@@ -421,7 +421,6 @@ static int
 writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct address_space *mapping = inode->i_mapping;
-       int wait = wbc->sync_mode == WB_SYNC_ALL;
        unsigned dirty;
        int ret;
 
@@ -439,7 +438,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
                 * We'll have another go at writing back this inode when we
                 * completed a full scan of b_io.
                 */
-               if (!wait) {
+               if (wbc->sync_mode != WB_SYNC_ALL) {
                        requeue_io(inode);
                        return 0;
                }
@@ -461,15 +460,20 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 
        ret = do_writepages(mapping, wbc);
 
-       /* Don't write the inode if only I_DIRTY_PAGES was set */
-       if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
-               int err = write_inode(inode, wait);
+       /*
+        * Make sure to wait on the data before writing out the metadata.
+        * This is important for filesystems that modify metadata on data
+        * I/O completion.
+        */
+       if (wbc->sync_mode == WB_SYNC_ALL) {
+               int err = filemap_fdatawait(mapping);
                if (ret == 0)
                        ret = err;
        }
 
-       if (wait) {
-               int err = filemap_fdatawait(mapping);
+       /* Don't write the inode if only I_DIRTY_PAGES was set */
+       if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
+               int err = write_inode(inode, wbc);
                if (ret == 0)
                        ret = err;
        }
index 84350e1be66d2b286e306b13395953557ace4be8..4e64352d49de1b442c5d627eb4ce3adba8c94f92 100644 (file)
@@ -976,122 +976,62 @@ out:
 }
 
 /**
- * gfs2_readlinki - return the contents of a symlink
- * @ip: the symlink's inode
- * @buf: a pointer to the buffer to be filled
- * @len: a pointer to the length of @buf
+ * gfs2_follow_link - Follow a symbolic link
+ * @dentry: The dentry of the link
+ * @nd: Data that we pass to vfs_follow_link()
  *
- * If @buf is too small, a piece of memory is kmalloc()ed and needs
- * to be freed by the caller.
+ * This can handle symlinks of any size.
  *
- * Returns: errno
+ * Returns: 0 on success or error code
  */
 
-static int gfs2_readlinki(struct gfs2_inode *ip, char **buf, unsigned int *len)
+static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
+       struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
        struct gfs2_holder i_gh;
        struct buffer_head *dibh;
        unsigned int x;
+       char *buf;
        int error;
 
        gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
        error = gfs2_glock_nq(&i_gh);
        if (error) {
                gfs2_holder_uninit(&i_gh);
-               return error;
+               nd_set_link(nd, ERR_PTR(error));
+               return NULL;
        }
 
        if (!ip->i_disksize) {
                gfs2_consist_inode(ip);
-               error = -EIO;
+               buf = ERR_PTR(-EIO);
                goto out;
        }
 
        error = gfs2_meta_inode_buffer(ip, &dibh);
-       if (error)
+       if (error) {
+               buf = ERR_PTR(error);
                goto out;
-
-       x = ip->i_disksize + 1;
-       if (x > *len) {
-               *buf = kmalloc(x, GFP_NOFS);
-               if (!*buf) {
-                       error = -ENOMEM;
-                       goto out_brelse;
-               }
        }
 
-       memcpy(*buf, dibh->b_data + sizeof(struct gfs2_dinode), x);
-       *len = x;
-
-out_brelse:
+       x = ip->i_disksize + 1;
+       buf = kmalloc(x, GFP_NOFS);
+       if (!buf)
+               buf = ERR_PTR(-ENOMEM);
+       else
+               memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), x);
        brelse(dibh);
 out:
        gfs2_glock_dq_uninit(&i_gh);
-       return error;
-}
-
-/**
- * gfs2_readlink - Read the value of a symlink
- * @dentry: the symlink
- * @buf: the buffer to read the symlink data into
- * @size: the size of the buffer
- *
- * Returns: errno
- */
-
-static int gfs2_readlink(struct dentry *dentry, char __user *user_buf,
-                        int user_size)
-{
-       struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
-       char array[GFS2_FAST_NAME_SIZE], *buf = array;
-       unsigned int len = GFS2_FAST_NAME_SIZE;
-       int error;
-
-       error = gfs2_readlinki(ip, &buf, &len);
-       if (error)
-               return error;
-
-       if (user_size > len - 1)
-               user_size = len - 1;
-
-       if (copy_to_user(user_buf, buf, user_size))
-               error = -EFAULT;
-       else
-               error = user_size;
-
-       if (buf != array)
-               kfree(buf);
-
-       return error;
+       nd_set_link(nd, buf);
+       return NULL;
 }
 
-/**
- * gfs2_follow_link - Follow a symbolic link
- * @dentry: The dentry of the link
- * @nd: Data that we pass to vfs_follow_link()
- *
- * This can handle symlinks of any size. It is optimised for symlinks
- * under GFS2_FAST_NAME_SIZE.
- *
- * Returns: 0 on success or error code
- */
-
-static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
 {
-       struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
-       char array[GFS2_FAST_NAME_SIZE], *buf = array;
-       unsigned int len = GFS2_FAST_NAME_SIZE;
-       int error;
-
-       error = gfs2_readlinki(ip, &buf, &len);
-       if (!error) {
-               error = vfs_follow_link(nd, buf);
-               if (buf != array)
-                       kfree(buf);
-       } else
-               path_put(&nd->path);
-
-       return ERR_PTR(error);
+       char *s = nd_get_link(nd);
+       if (!IS_ERR(s))
+               kfree(s);
 }
 
 /**
@@ -1426,8 +1366,9 @@ const struct inode_operations gfs2_dir_iops = {
 };
 
 const struct inode_operations gfs2_symlink_iops = {
-       .readlink = gfs2_readlink,
+       .readlink = generic_readlink,
        .follow_link = gfs2_follow_link,
+       .put_link = gfs2_put_link,
        .permission = gfs2_permission,
        .setattr = gfs2_setattr,
        .getattr = gfs2_getattr,
index e3bf6eab8750011a9e582a0f465bbf3dc49b8612..6dbcbad6ab1783449d66a134bb718bbc4037a81f 100644 (file)
@@ -1083,7 +1083,7 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change,
        }
 }
 
-int gfs2_quota_sync(struct super_block *sb, int type)
+int gfs2_quota_sync(struct super_block *sb, int type, int wait)
 {
        struct gfs2_sbd *sdp = sb->s_fs_info;
        struct gfs2_quota_data **qda;
@@ -1127,6 +1127,11 @@ int gfs2_quota_sync(struct super_block *sb, int type)
        return error;
 }
 
+static int gfs2_quota_sync_timeo(struct super_block *sb, int type)
+{
+       return gfs2_quota_sync(sb, type, 0);
+}
+
 int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id)
 {
        struct gfs2_quota_data *qd;
@@ -1382,7 +1387,7 @@ int gfs2_quotad(void *data)
                                           &tune->gt_statfs_quantum);
 
                /* Update quota file */
-               quotad_check_timeo(sdp, "sync", gfs2_quota_sync, t,
+               quotad_check_timeo(sdp, "sync", gfs2_quota_sync_timeo, t,
                                   &quotad_timeo, &tune->gt_quota_quantum);
 
                /* Check for & recover partially truncated inodes */
index e271fa07ad0206dfbda9cb33ad01c9cbd0d7c3ef..195f60c8bd144b6ae5040c878e4f14305cafccc4 100644 (file)
@@ -25,7 +25,7 @@ extern int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid);
 extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change,
                              u32 uid, u32 gid);
 
-extern int gfs2_quota_sync(struct super_block *sb, int type);
+extern int gfs2_quota_sync(struct super_block *sb, int type, int wait);
 extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id);
 
 extern int gfs2_quota_init(struct gfs2_sbd *sdp);
index e5e22629da67cd5aa1a987ca76a92f18037fd694..50aac606b990ccb47b148ec75101e9e516b8b563 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/crc32.h>
 #include <linux/time.h>
 #include <linux/wait.h>
+#include <linux/writeback.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -711,7 +712,7 @@ void gfs2_unfreeze_fs(struct gfs2_sbd *sdp)
  * Returns: errno
  */
 
-static int gfs2_write_inode(struct inode *inode, int sync)
+static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -745,7 +746,7 @@ static int gfs2_write_inode(struct inode *inode, int sync)
 do_unlock:
        gfs2_glock_dq_uninit(&gh);
 do_flush:
-       if (sync != 0)
+       if (wbc->sync_mode == WB_SYNC_ALL)
                gfs2_log_flush(GFS2_SB(inode), ip->i_gl);
        return ret;
 }
@@ -763,7 +764,7 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
        int error;
 
        flush_workqueue(gfs2_delete_workqueue);
-       gfs2_quota_sync(sdp->sd_vfs, 0);
+       gfs2_quota_sync(sdp->sd_vfs, 0, 1);
        gfs2_statfs_sync(sdp->sd_vfs, 0);
 
        error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED, GL_NOCACHE,
index a0db1c94317dd5b72e0f3c3a95f82d1126d8f4e9..b5f1a46133c8f208804b35876823d1270a625e7c 100644 (file)
@@ -167,7 +167,7 @@ static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf,
        if (simple_strtol(buf, NULL, 0) != 1)
                return -EINVAL;
 
-       gfs2_quota_sync(sdp->sd_vfs, 0);
+       gfs2_quota_sync(sdp->sd_vfs, 0, 1);
        return len;
 }
 
index 052387e116716d014ff9a398a76597d6fb926131..fe35e3b626c4b102bec8acb0ec8a33d0e1f288ae 100644 (file)
@@ -188,7 +188,7 @@ extern const struct address_space_operations hfs_btree_aops;
 
 extern struct inode *hfs_new_inode(struct inode *, struct qstr *, int);
 extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
-extern int hfs_write_inode(struct inode *, int);
+extern int hfs_write_inode(struct inode *, struct writeback_control *);
 extern int hfs_inode_setattr(struct dentry *, struct iattr *);
 extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
                        __be32 log_size, __be32 phys_size, u32 clump_size);
index a1cbff2b4d99066718f9dd0a44c86cd4ec20de48..14f5cb1b9fdcfcd10116d065535831af20a3603a 100644 (file)
@@ -381,7 +381,7 @@ void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext,
                                         HFS_SB(inode->i_sb)->alloc_blksz);
 }
 
-int hfs_write_inode(struct inode *inode, int unused)
+int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct inode *main_inode = inode;
        struct hfs_find_data fd;
index 43022f3d514871d9f2405ff32eacbd1319f8e26a..74b473a8ef929f3c1abdd7ac85019ce4e87494ba 100644 (file)
@@ -87,7 +87,8 @@ bad_inode:
        return ERR_PTR(err);
 }
 
-static int hfsplus_write_inode(struct inode *inode, int unused)
+static int hfsplus_write_inode(struct inode *inode,
+               struct writeback_control *wbc)
 {
        struct hfsplus_vh *vhdr;
        int ret = 0;
index 1aa88c4e0964b5acad9f37c3928167406493e564..6a2f04bf3df007249047523e10a098fd2b43c603 100644 (file)
@@ -353,7 +353,7 @@ int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
 }
 
 int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
-            unsigned len, char *buf)
+            unsigned len, const char *buf)
 {
        struct buffer_head *bh;
        char *data;
index 940d6d150beec4d609785c16aadb87a49b1689f7..67d9d36b3d5fa05534b0a0abe320da25292e839d 100644 (file)
@@ -20,8 +20,8 @@ static int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
 
        if (l == 1) if (qstr->name[0]=='.') goto x;
        if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x;
-       hpfs_adjust_length((char *)qstr->name, &l);
-       /*if (hpfs_chk_name((char *)qstr->name,&l))*/
+       hpfs_adjust_length(qstr->name, &l);
+       /*if (hpfs_chk_name(qstr->name,&l))*/
                /*return -ENAMETOOLONG;*/
                /*return -ENOENT;*/
        x:
@@ -38,14 +38,16 @@ static int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qst
 {
        unsigned al=a->len;
        unsigned bl=b->len;
-       hpfs_adjust_length((char *)a->name, &al);
-       /*hpfs_adjust_length((char *)b->name, &bl);*/
+       hpfs_adjust_length(a->name, &al);
+       /*hpfs_adjust_length(b->name, &bl);*/
        /* 'a' is the qstr of an already existing dentry, so the name
         * must be valid. 'b' must be validated first.
         */
 
-       if (hpfs_chk_name((char *)b->name, &bl)) return 1;
-       if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1;
+       if (hpfs_chk_name(b->name, &bl))
+               return 1;
+       if (hpfs_compare_names(dentry->d_sb, a->name, al, b->name, bl, 0))
+               return 1;
        return 0;
 }
 
index 8865c94f55f602f19cb3b68182fc70bd41fcb4e4..26e3964a4b8c19a7075d0ef623ab041392c78b53 100644 (file)
@@ -59,7 +59,7 @@ static int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        struct hpfs_dirent *de;
        int lc;
        long old_pos;
-       char *tempname;
+       unsigned char *tempname;
        int c1, c2 = 0;
        int ret = 0;
 
@@ -158,11 +158,11 @@ static int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
                if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode, DT_UNKNOWN) < 0) {
                        filp->f_pos = old_pos;
-                       if (tempname != (char *)de->name) kfree(tempname);
+                       if (tempname != de->name) kfree(tempname);
                        hpfs_brelse4(&qbh);
                        goto out;
                }
-               if (tempname != (char *)de->name) kfree(tempname);
+               if (tempname != de->name) kfree(tempname);
                hpfs_brelse4(&qbh);
        }
 out:
@@ -187,7 +187,7 @@ out:
 
 struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct quad_buffer_head qbh;
        struct hpfs_dirent *de;
@@ -197,7 +197,7 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct name
        struct hpfs_inode_info *hpfs_result;
 
        lock_kernel();
-       if ((err = hpfs_chk_name((char *)name, &len))) {
+       if ((err = hpfs_chk_name(name, &len))) {
                if (err == -ENAMETOOLONG) {
                        unlock_kernel();
                        return ERR_PTR(-ENAMETOOLONG);
@@ -209,7 +209,7 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct name
         * '.' and '..' will never be passed here.
         */
 
-       de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *) name, len, NULL, &qbh);
+       de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, NULL, &qbh);
 
        /*
         * This is not really a bailout, just means file not found.
@@ -250,7 +250,7 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct name
        hpfs_result = hpfs_i(result);
        if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino;
 
-       hpfs_decide_conv(result, (char *)name, len);
+       hpfs_decide_conv(result, name, len);
 
        if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
                hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
index fe83c2b7d2d8bb8899ca47e0f90ce5741ff3bbc4..9b2ffadfc8c42dd9a39f86a0bdedb6a8dbbd0df5 100644 (file)
@@ -158,7 +158,8 @@ static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno
 
 /* Add an entry to dnode and don't care if it grows over 2048 bytes */
 
-struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d, unsigned char *name,
+struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d,
+                               const unsigned char *name,
                                unsigned namelen, secno down_ptr)
 {
        struct hpfs_dirent *de;
@@ -223,7 +224,7 @@ static void fix_up_ptrs(struct super_block *s, struct dnode *d)
 /* Add an entry to dnode and do dnode splitting if required */
 
 static int hpfs_add_to_dnode(struct inode *i, dnode_secno dno,
-                            unsigned char *name, unsigned namelen,
+                            const unsigned char *name, unsigned namelen,
                             struct hpfs_dirent *new_de, dnode_secno down_ptr)
 {
        struct quad_buffer_head qbh, qbh1, qbh2;
@@ -231,7 +232,7 @@ static int hpfs_add_to_dnode(struct inode *i, dnode_secno dno,
        dnode_secno adno, rdno;
        struct hpfs_dirent *de;
        struct hpfs_dirent nde;
-       char *nname;
+       unsigned char *nname;
        int h;
        int pos;
        struct buffer_head *bh;
@@ -305,7 +306,9 @@ static int hpfs_add_to_dnode(struct inode *i, dnode_secno dno,
                pos++;
        }
        copy_de(new_de = &nde, de);
-       memcpy(name = nname, de->name, namelen = de->namelen);
+       memcpy(nname, de->name, de->namelen);
+       name = nname;
+       namelen = de->namelen;
        for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, 4);
        down_ptr = adno;
        set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0);
@@ -368,7 +371,8 @@ static int hpfs_add_to_dnode(struct inode *i, dnode_secno dno,
  * I hope, now it's finally bug-free.
  */
 
-int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
+int hpfs_add_dirent(struct inode *i,
+                   const unsigned char *name, unsigned namelen,
                    struct hpfs_dirent *new_de, int cdepth)
 {
        struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
@@ -897,7 +901,8 @@ struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
 
 /* Find a dirent in tree */
 
-struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, unsigned len,
+struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
+                              const unsigned char *name, unsigned len,
                               dnode_secno *dd, struct quad_buffer_head *qbh)
 {
        struct dnode *dnode;
@@ -988,8 +993,8 @@ void hpfs_remove_dtree(struct super_block *s, dnode_secno dno)
 struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
                                     struct fnode *f, struct quad_buffer_head *qbh)
 {
-       char *name1;
-       char *name2;
+       unsigned char *name1;
+       unsigned char *name2;
        int name1len, name2len;
        struct dnode *d;
        dnode_secno dno, downd;
index 547a8384571fcc9d1b95282933879125db098efa..45e53d972b42ed27e931c5fa01ae5fb194b0c5f2 100644 (file)
@@ -62,8 +62,8 @@ static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size)
        return ret;
 }
 
-static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data,
-                           int size)
+static void set_indirect_ea(struct super_block *s, int ano, secno a,
+                           const char *data, int size)
 {
        hpfs_ea_write(s, a, ano, 0, size, data);
 }
@@ -186,7 +186,8 @@ char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *si
  * This driver can't change sizes of eas ('cause I just don't need it).
  */
 
-void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size)
+void hpfs_set_ea(struct inode *inode, struct fnode *fnode, const char *key,
+                const char *data, int size)
 {
        fnode_secno fno = inode->i_ino;
        struct super_block *s = inode->i_sb;
index 701ca54c0867fe482b9c33fdb6615a015114fa95..97bf738cd5d65117b4610a707738dd9accd8c2eb 100644 (file)
@@ -215,7 +215,7 @@ secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_heade
 secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned);
 void hpfs_remove_btree(struct super_block *, struct bplus_header *);
 int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *);
-int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *);
+int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, const char *);
 void hpfs_ea_remove(struct super_block *, secno, int, unsigned);
 void hpfs_truncate_btree(struct super_block *, secno, int, unsigned);
 void hpfs_remove_fnode(struct super_block *, fnode_secno fno);
@@ -244,13 +244,17 @@ extern const struct file_operations hpfs_dir_ops;
 
 void hpfs_add_pos(struct inode *, loff_t *);
 void hpfs_del_pos(struct inode *, loff_t *);
-struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno);
-int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int);
+struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *,
+                               const unsigned char *, unsigned, secno);
+int hpfs_add_dirent(struct inode *, const unsigned char *, unsigned,
+                   struct hpfs_dirent *, int);
 int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
 void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
 dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
 struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *);
-struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *);
+struct hpfs_dirent *map_dirent(struct inode *, dnode_secno,
+                              const unsigned char *, unsigned, dnode_secno *,
+                              struct quad_buffer_head *);
 void hpfs_remove_dtree(struct super_block *, dnode_secno);
 struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *);
 
@@ -259,7 +263,8 @@ struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct f
 void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned);
 int hpfs_read_ea(struct super_block *, struct fnode *, char *, char *, int);
 char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *);
-void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
+void hpfs_set_ea(struct inode *, struct fnode *, const char *,
+                const char *, int);
 
 /* file.c */
 
@@ -282,7 +287,7 @@ void hpfs_delete_inode(struct inode *);
 
 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 *);
-char *hpfs_load_code_page(struct super_block *, secno);
+unsigned char *hpfs_load_code_page(struct super_block *, secno);
 secno *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 **);
@@ -292,12 +297,13 @@ dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino);
 /* name.c */
 
 unsigned char hpfs_upcase(unsigned char *, unsigned char);
-int hpfs_chk_name(unsigned char *, unsigned *);
-char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
-int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int);
-int hpfs_is_name_long(unsigned char *, unsigned);
-void hpfs_adjust_length(unsigned char *, unsigned *);
-void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
+int hpfs_chk_name(const unsigned char *, unsigned *);
+unsigned char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
+int hpfs_compare_names(struct super_block *, const unsigned char *, unsigned,
+                      const unsigned char *, unsigned, int);
+int hpfs_is_name_long(const unsigned char *, unsigned);
+void hpfs_adjust_length(const unsigned char *, unsigned *);
+void hpfs_decide_conv(struct inode *, const unsigned char *, unsigned);
 
 /* namei.c */
 
index fe703ae46bc773f95a8502f12ccae915531d4271..ff90affb94e16ebdd2b2a76156dab515d4a0a050 100644 (file)
@@ -46,7 +46,7 @@ void hpfs_read_inode(struct inode *i)
        struct fnode *fnode;
        struct super_block *sb = i->i_sb;
        struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
-       unsigned char *ea;
+       void *ea;
        int ea_size;
 
        if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
@@ -112,7 +112,7 @@ void hpfs_read_inode(struct inode *i)
                }
        }
        if (fnode->dirflag) {
-               unsigned n_dnodes, n_subdirs;
+               int n_dnodes, n_subdirs;
                i->i_mode |= S_IFDIR;
                i->i_op = &hpfs_dir_iops;
                i->i_fop = &hpfs_dir_ops;
index c4724589b2eba1854f9d5afc7a274c1a9beed066..840d033ecee832561f7f9ed91cec78527a62e38f 100644 (file)
@@ -35,7 +35,7 @@ unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
  * lowercasing table
  */
 
-char *hpfs_load_code_page(struct super_block *s, secno cps)
+unsigned char *hpfs_load_code_page(struct super_block *s, secno cps)
 {
        struct buffer_head *bh;
        secno cpds;
@@ -71,7 +71,7 @@ char *hpfs_load_code_page(struct super_block *s, secno cps)
                brelse(bh);
                return NULL;
        }
-       ptr = (char *)cpd + cpd->offs[cpi] + 6;
+       ptr = (unsigned char *)cpd + cpd->offs[cpi] + 6;
        if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
                printk("HPFS: out of memory for code page table\n");
                brelse(bh);
@@ -217,7 +217,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
        if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
                if (hpfs_sb(s)->sb_chk) {
                        unsigned p, pp = 0;
-                       unsigned char *d = (char *)dnode;
+                       unsigned char *d = (unsigned char *)dnode;
                        int b = 0;
                        if (dnode->magic != DNODE_MAGIC) {
                                hpfs_error(s, "bad magic on dnode %08x", secno);
index 1f4a964384eb36f5286e5d917f4e7d0838b74b17..f24736d7a439218aa238f75df3338c3e3e64744f 100644 (file)
@@ -8,16 +8,16 @@
 
 #include "hpfs_fn.h"
 
-static char *text_postfix[]={
+static const char *text_postfix[]={
 ".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
 ".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
 ".RC", ".TEX", ".TXT", ".Y", ""};
 
-static char *text_prefix[]={
+static const char *text_prefix[]={
 "AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
 "MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
 
-void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
+void hpfs_decide_conv(struct inode *inode, const unsigned char *name, unsigned len)
 {
        struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
        int i;
@@ -71,7 +71,7 @@ static inline unsigned char locase(unsigned char *dir, unsigned char a)
        return dir[a];
 }
 
-int hpfs_chk_name(unsigned char *name, unsigned *len)
+int hpfs_chk_name(const unsigned char *name, unsigned *len)
 {
        int i;
        if (*len > 254) return -ENAMETOOLONG;
@@ -83,10 +83,10 @@ int hpfs_chk_name(unsigned char *name, unsigned *len)
        return 0;
 }
 
-char *hpfs_translate_name(struct super_block *s, unsigned char *from,
+unsigned char *hpfs_translate_name(struct super_block *s, unsigned char *from,
                          unsigned len, int lc, int lng)
 {
-       char *to;
+       unsigned char *to;
        int i;
        if (hpfs_sb(s)->sb_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
                printk("HPFS: Long name flag mismatch - name ");
@@ -103,8 +103,9 @@ char *hpfs_translate_name(struct super_block *s, unsigned char *from,
        return to;
 }
 
-int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
-                      unsigned char *n2, unsigned l2, int last)
+int hpfs_compare_names(struct super_block *s,
+                      const unsigned char *n1, unsigned l1,
+                      const unsigned char *n2, unsigned l2, int last)
 {
        unsigned l = l1 < l2 ? l1 : l2;
        unsigned i;
@@ -120,7 +121,7 @@ int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
        return 0;
 }
 
-int hpfs_is_name_long(unsigned char *name, unsigned len)
+int hpfs_is_name_long(const unsigned char *name, unsigned len)
 {
        int i,j;
        for (i = 0; i < len && name[i] != '.'; i++)
@@ -134,7 +135,7 @@ int hpfs_is_name_long(unsigned char *name, unsigned len)
 
 /* OS/2 clears dots and spaces at the end of file name, so we have to */
 
-void hpfs_adjust_length(unsigned char *name, unsigned *len)
+void hpfs_adjust_length(const unsigned char *name, unsigned *len)
 {
        if (!*len) return;
        if (*len == 1 && name[0] == '.') return;
index 82b9c4ba9ed08b07eda6832d6a5953581f515967..11c2b4080f65d335d373e599bc374c9122d543a0 100644 (file)
@@ -11,7 +11,7 @@
 
 static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct quad_buffer_head qbh0;
        struct buffer_head *bh;
@@ -24,7 +24,7 @@ static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        int r;
        struct hpfs_dirent dee;
        int err;
-       if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+       if ((err = hpfs_chk_name(name, &len))) return err==-ENOENT ? -EINVAL : err;
        lock_kernel();
        err = -ENOSPC;
        fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
@@ -62,7 +62,7 @@ static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
                result->i_mode &= ~0222;
 
        mutex_lock(&hpfs_i(dir)->i_mutex);
-       r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+       r = hpfs_add_dirent(dir, name, len, &dee, 0);
        if (r == 1)
                goto bail3;
        if (r == -1) {
@@ -121,7 +121,7 @@ bail:
 
 static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct inode *result = NULL;
        struct buffer_head *bh;
@@ -130,7 +130,7 @@ static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struc
        int r;
        struct hpfs_dirent dee;
        int err;
-       if ((err = hpfs_chk_name((char *)name, &len)))
+       if ((err = hpfs_chk_name(name, &len)))
                return err==-ENOENT ? -EINVAL : err;
        lock_kernel();
        err = -ENOSPC;
@@ -155,7 +155,7 @@ static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struc
        result->i_op = &hpfs_file_iops;
        result->i_fop = &hpfs_file_ops;
        result->i_nlink = 1;
-       hpfs_decide_conv(result, (char *)name, len);
+       hpfs_decide_conv(result, name, len);
        hpfs_i(result)->i_parent_dir = dir->i_ino;
        result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
        result->i_ctime.tv_nsec = 0;
@@ -170,7 +170,7 @@ static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struc
        hpfs_i(result)->mmu_private = 0;
 
        mutex_lock(&hpfs_i(dir)->i_mutex);
-       r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+       r = hpfs_add_dirent(dir, name, len, &dee, 0);
        if (r == 1)
                goto bail2;
        if (r == -1) {
@@ -211,7 +211,7 @@ bail:
 
 static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct buffer_head *bh;
        struct fnode *fnode;
@@ -220,7 +220,7 @@ static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
        struct hpfs_dirent dee;
        struct inode *result = NULL;
        int err;
-       if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+       if ((err = hpfs_chk_name(name, &len))) return err==-ENOENT ? -EINVAL : err;
        if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM;
        if (!new_valid_dev(rdev))
                return -EINVAL;
@@ -256,7 +256,7 @@ static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
        init_special_inode(result, mode, rdev);
 
        mutex_lock(&hpfs_i(dir)->i_mutex);
-       r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+       r = hpfs_add_dirent(dir, name, len, &dee, 0);
        if (r == 1)
                goto bail2;
        if (r == -1) {
@@ -289,7 +289,7 @@ bail:
 
 static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct buffer_head *bh;
        struct fnode *fnode;
@@ -298,7 +298,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
        struct hpfs_dirent dee;
        struct inode *result;
        int err;
-       if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+       if ((err = hpfs_chk_name(name, &len))) return err==-ENOENT ? -EINVAL : err;
        lock_kernel();
        if (hpfs_sb(dir->i_sb)->sb_eas < 2) {
                unlock_kernel();
@@ -335,7 +335,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
        result->i_data.a_ops = &hpfs_symlink_aops;
 
        mutex_lock(&hpfs_i(dir)->i_mutex);
-       r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+       r = hpfs_add_dirent(dir, name, len, &dee, 0);
        if (r == 1)
                goto bail2;
        if (r == -1) {
@@ -345,7 +345,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
        fnode->len = len;
        memcpy(fnode->name, name, len > 15 ? 15 : len);
        fnode->up = dir->i_ino;
-       hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink));
+       hpfs_set_ea(result, fnode, "SYMLINK", symlink, strlen(symlink));
        mark_buffer_dirty(bh);
        brelse(bh);
 
@@ -369,7 +369,7 @@ bail:
 
 static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct quad_buffer_head qbh;
        struct hpfs_dirent *de;
@@ -381,12 +381,12 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
        int err;
 
        lock_kernel();
-       hpfs_adjust_length((char *)name, &len);
+       hpfs_adjust_length(name, &len);
 again:
        mutex_lock(&hpfs_i(inode)->i_parent_mutex);
        mutex_lock(&hpfs_i(dir)->i_mutex);
        err = -ENOENT;
-       de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh);
+       de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
        if (!de)
                goto out;
 
@@ -413,22 +413,25 @@ again:
 
                mutex_unlock(&hpfs_i(dir)->i_mutex);
                mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
-               d_drop(dentry);
-               spin_lock(&dentry->d_lock);
-               if (atomic_read(&dentry->d_count) > 1 ||
-                   generic_permission(inode, MAY_WRITE, NULL) ||
+               dentry_unhash(dentry);
+               if (!d_unhashed(dentry)) {
+                       dput(dentry);
+                       unlock_kernel();
+                       return -ENOSPC;
+               }
+               if (generic_permission(inode, MAY_WRITE, NULL) ||
                    !S_ISREG(inode->i_mode) ||
                    get_write_access(inode)) {
-                       spin_unlock(&dentry->d_lock);
                        d_rehash(dentry);
+                       dput(dentry);
                } else {
                        struct iattr newattrs;
-                       spin_unlock(&dentry->d_lock);
                        /*printk("HPFS: truncating file before delete.\n");*/
                        newattrs.ia_size = 0;
                        newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
                        err = notify_change(dentry, &newattrs);
                        put_write_access(inode);
+                       dput(dentry);
                        if (!err)
                                goto again;
                }
@@ -451,7 +454,7 @@ out:
 
 static int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        unsigned len = dentry->d_name.len;
        struct quad_buffer_head qbh;
        struct hpfs_dirent *de;
@@ -462,12 +465,12 @@ static int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
        int err;
        int r;
 
-       hpfs_adjust_length((char *)name, &len);
+       hpfs_adjust_length(name, &len);
        lock_kernel();
        mutex_lock(&hpfs_i(inode)->i_parent_mutex);
        mutex_lock(&hpfs_i(dir)->i_mutex);
        err = -ENOENT;
-       de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh);
+       de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
        if (!de)
                goto out;
 
@@ -546,10 +549,10 @@ const struct address_space_operations hpfs_symlink_aops = {
 static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                struct inode *new_dir, struct dentry *new_dentry)
 {
-       char *old_name = (char *)old_dentry->d_name.name;
-       int old_len = old_dentry->d_name.len;
-       char *new_name = (char *)new_dentry->d_name.name;
-       int new_len = new_dentry->d_name.len;
+       const unsigned char *old_name = old_dentry->d_name.name;
+       unsigned old_len = old_dentry->d_name.len;
+       const unsigned char *new_name = new_dentry->d_name.name;
+       unsigned new_len = new_dentry->d_name.len;
        struct inode *i = old_dentry->d_inode;
        struct inode *new_inode = new_dentry->d_inode;
        struct quad_buffer_head qbh, qbh1;
@@ -560,9 +563,9 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct buffer_head *bh;
        struct fnode *fnode;
        int err;
-       if ((err = hpfs_chk_name((char *)new_name, &new_len))) return err;
+       if ((err = hpfs_chk_name(new_name, &new_len))) return err;
        err = 0;
-       hpfs_adjust_length((char *)old_name, &old_len);
+       hpfs_adjust_length(old_name, &old_len);
 
        lock_kernel();
        /* order doesn't matter, due to VFS exclusion */
@@ -579,7 +582,7 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto end1;
        }
 
-       if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
+       if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, old_name, old_len, &dno, &qbh))) {
                hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
                err = -ENOENT;
                goto end1;
@@ -590,7 +593,7 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (new_inode) {
                int r;
                if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
-                       if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
+                       if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, new_name, new_len, NULL, &qbh1))) {
                                clear_nlink(new_inode);
                                copy_de(nde, &de);
                                memcpy(nde->name, new_name, new_len);
@@ -618,7 +621,7 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        }
        
        if (new_dir == old_dir)
-               if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
+               if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, old_name, old_len, &dno, &qbh))) {
                        hpfs_unlock_creation(i->i_sb);
                        hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
                        err = -ENOENT;
@@ -648,7 +651,7 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                brelse(bh);
        }
        hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv;
-       hpfs_decide_conv(i, (char *)new_name, new_len);
+       hpfs_decide_conv(i, new_name, new_len);
 end1:
        if (old_dir != new_dir)
                mutex_unlock(&hpfs_i(new_dir)->i_mutex);
index 7239efc690d833cf9e4a507916a5819270c1f267..2e4dfa8593da4c047a1a9028f6d8f8ce2b8b78bd 100644 (file)
@@ -718,7 +718,7 @@ static int hppfs_fill_super(struct super_block *sb, void *d, int silent)
        struct vfsmount *proc_mnt;
        int err = -ENOENT;
 
-       proc_mnt = do_kern_mount("proc", 0, "proc", NULL);
+       proc_mnt = mntget(current->nsproxy->pid_ns->proc_mnt);
        if (IS_ERR(proc_mnt))
                goto out;
 
index 03dfeb2e39287a75b2fccbe6c71d1451ebade3a0..407bf392e20a67e9494537258ffe2ad73b92a8cb 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/mm.h>
 #include <linux/dcache.h>
 #include <linux/init.h>
-#include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/writeback.h>
 #include <linux/module.h>
@@ -314,7 +313,6 @@ void clear_inode(struct inode *inode)
        BUG_ON(!(inode->i_state & I_FREEING));
        BUG_ON(inode->i_state & I_CLEAR);
        inode_sync_wait(inode);
-       vfs_dq_drop(inode);
        if (inode->i_sb->s_op->clear_inode)
                inode->i_sb->s_op->clear_inode(inode);
        if (S_ISBLK(inode->i_mode) && inode->i_bdev)
@@ -1211,8 +1209,6 @@ void generic_delete_inode(struct inode *inode)
 
        if (op->delete_inode) {
                void (*delete)(struct inode *) = op->delete_inode;
-               if (!is_bad_inode(inode))
-                       vfs_dq_init(inode);
                /* Filesystems implementing their own
                 * s_op->delete_inode are required to call
                 * truncate_inode_pages and clear_inode()
index e96a1667d749d5efa59b659fea5d1f0057ffb973..8a03a5447bdf392a826b088f7329c68af32f3d2e 100644 (file)
@@ -70,6 +70,8 @@ extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int);
 
 extern void __init mnt_init(void);
 
+extern spinlock_t vfsmount_lock;
+
 /*
  * fs_struct.c
  */
index 4bd882548c456bc496c9facab237e12445efaed1..2c90e3ef625f35ae0bfdfa74270273c9d35a1695 100644 (file)
@@ -862,12 +862,12 @@ restart_loop:
                /* A buffer which has been freed while still being
                 * journaled by a previous transaction may end up still
                 * being dirty here, but we want to avoid writing back
-                * that buffer in the future now that the last use has
-                * been committed.  That's not only a performance gain,
-                * it also stops aliasing problems if the buffer is left
-                * behind for writeback and gets reallocated for another
+                * that buffer in the future after the "add to orphan"
+                * operation been committed,  That's not only a performance
+                * gain, it also stops aliasing problems if the buffer is
+                * left behind for writeback and gets reallocated for another
                 * use in a different page. */
-               if (buffer_freed(bh)) {
+               if (buffer_freed(bh) && !jh->b_next_transaction) {
                        clear_buffer_freed(bh);
                        clear_buffer_jbddirty(bh);
                }
index 006f9ad838a26aed53ae9f1d54a9065d781e3f2a..99e9fea11077effbc1be2b8a7d244c89d4b823c4 100644 (file)
@@ -1864,6 +1864,21 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
        if (!jh)
                goto zap_buffer_no_jh;
 
+       /*
+        * We cannot remove the buffer from checkpoint lists until the
+        * transaction adding inode to orphan list (let's call it T)
+        * is committed.  Otherwise if the transaction changing the
+        * buffer would be cleaned from the journal before T is
+        * committed, a crash will cause that the correct contents of
+        * the buffer will be lost.  On the other hand we have to
+        * clear the buffer dirty bit at latest at the moment when the
+        * transaction marking the buffer as freed in the filesystem
+        * structures is committed because from that moment on the
+        * buffer can be reallocated and used by a different page.
+        * Since the block hasn't been freed yet but the inode has
+        * already been added to orphan list, it is safe for us to add
+        * the buffer to BJ_Forget list of the newest transaction.
+        */
        transaction = jh->b_transaction;
        if (transaction == NULL) {
                /* First case: not on any transaction.  If it
@@ -1929,16 +1944,15 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
                        goto zap_buffer;
                }
                /*
-                * If it is committing, we simply cannot touch it.  We
-                * can remove it's next_transaction pointer from the
-                * running transaction if that is set, but nothing
-                * else. */
+                * The buffer is committing, we simply cannot touch
+                * it. So we just set j_next_transaction to the
+                * running transaction (if there is one) and mark
+                * buffer as freed so that commit code knows it should
+                * clear dirty bits when it is done with the buffer.
+                */
                set_buffer_freed(bh);
-               if (jh->b_next_transaction) {
-                       J_ASSERT(jh->b_next_transaction ==
-                                       journal->j_running_transaction);
-                       jh->b_next_transaction = NULL;
-               }
+               if (journal->j_running_transaction && buffer_jbddirty(bh))
+                       jh->b_next_transaction = journal->j_running_transaction;
                journal_put_journal_head(jh);
                spin_unlock(&journal->j_list_lock);
                jbd_unlock_bh_state(bh);
@@ -2120,7 +2134,7 @@ void journal_file_buffer(struct journal_head *jh,
  */
 void __journal_refile_buffer(struct journal_head *jh)
 {
-       int was_dirty;
+       int was_dirty, jlist;
        struct buffer_head *bh = jh2bh(jh);
 
        J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
@@ -2142,8 +2156,13 @@ void __journal_refile_buffer(struct journal_head *jh)
        __journal_temp_unlink_buffer(jh);
        jh->b_transaction = jh->b_next_transaction;
        jh->b_next_transaction = NULL;
-       __journal_file_buffer(jh, jh->b_transaction,
-                               jh->b_modified ? BJ_Metadata : BJ_Reserved);
+       if (buffer_freed(bh))
+               jlist = BJ_Forget;
+       else if (jh->b_modified)
+               jlist = BJ_Metadata;
+       else
+               jlist = BJ_Reserved;
+       __journal_file_buffer(jh, jh->b_transaction, jlist);
        J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
 
        if (was_dirty)
index 886849370950f462627f0a9d2df692ec4d117d97..30beb11ef928f35c75d52e4d1f68857fe370172c 100644 (file)
@@ -507,6 +507,7 @@ int jbd2_cleanup_journal_tail(journal_t *journal)
        if (blocknr < journal->j_tail)
                freed = freed + journal->j_last - journal->j_first;
 
+       trace_jbd2_cleanup_journal_tail(journal, first_tid, blocknr, freed);
        jbd_debug(1,
                  "Cleaning journal tail from %d to %d (offset %lu), "
                  "freeing %lu\n",
index 1bc74b6f26d2cbb9d159f9f49aef102fa7dda30e..671da7fb7ffd528fc9ecb545298603b4daf701f8 100644 (file)
@@ -883,8 +883,7 @@ restart_loop:
                spin_unlock(&journal->j_list_lock);
                bh = jh2bh(jh);
                jbd_lock_bh_state(bh);
-               J_ASSERT_JH(jh, jh->b_transaction == commit_transaction ||
-                       jh->b_transaction == journal->j_running_transaction);
+               J_ASSERT_JH(jh, jh->b_transaction == commit_transaction);
 
                /*
                 * If there is undo-protected committed data against
@@ -930,12 +929,12 @@ restart_loop:
                /* A buffer which has been freed while still being
                 * journaled by a previous transaction may end up still
                 * being dirty here, but we want to avoid writing back
-                * that buffer in the future now that the last use has
-                * been committed.  That's not only a performance gain,
-                * it also stops aliasing problems if the buffer is left
-                * behind for writeback and gets reallocated for another
+                * that buffer in the future after the "add to orphan"
+                * operation been committed,  That's not only a performance
+                * gain, it also stops aliasing problems if the buffer is
+                * left behind for writeback and gets reallocated for another
                 * use in a different page. */
-               if (buffer_freed(bh)) {
+               if (buffer_freed(bh) && !jh->b_next_transaction) {
                        clear_buffer_freed(bh);
                        clear_buffer_jbddirty(bh);
                }
index ac0d027595d00bf28e55f00b040d26e295023602..c03d4dce4d76a0751ae9eee57066a77f0bfb0fb6 100644 (file)
@@ -39,6 +39,8 @@
 #include <linux/seq_file.h>
 #include <linux/math64.h>
 #include <linux/hash.h>
+#include <linux/log2.h>
+#include <linux/vmalloc.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/jbd2.h>
@@ -93,6 +95,7 @@ EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate);
 
 static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
 static void __journal_abort_soft (journal_t *journal, int errno);
+static int jbd2_journal_create_slab(size_t slab_size);
 
 /*
  * Helper function used to manage commit timeouts
@@ -1248,6 +1251,13 @@ int jbd2_journal_load(journal_t *journal)
                }
        }
 
+       /*
+        * Create a slab for this blocksize
+        */
+       err = jbd2_journal_create_slab(be32_to_cpu(sb->s_blocksize));
+       if (err)
+               return err;
+
        /* Let the recovery code check whether it needs to recover any
         * data from the journal. */
        if (jbd2_journal_recover(journal))
@@ -1806,6 +1816,127 @@ size_t journal_tag_bytes(journal_t *journal)
                return JBD2_TAG_SIZE32;
 }
 
+/*
+ * JBD memory management
+ *
+ * These functions are used to allocate block-sized chunks of memory
+ * used for making copies of buffer_head data.  Very often it will be
+ * page-sized chunks of data, but sometimes it will be in
+ * sub-page-size chunks.  (For example, 16k pages on Power systems
+ * with a 4k block file system.)  For blocks smaller than a page, we
+ * use a SLAB allocator.  There are slab caches for each block size,
+ * which are allocated at mount time, if necessary, and we only free
+ * (all of) the slab caches when/if the jbd2 module is unloaded.  For
+ * this reason we don't need to a mutex to protect access to
+ * jbd2_slab[] allocating or releasing memory; only in
+ * jbd2_journal_create_slab().
+ */
+#define JBD2_MAX_SLABS 8
+static struct kmem_cache *jbd2_slab[JBD2_MAX_SLABS];
+static DECLARE_MUTEX(jbd2_slab_create_sem);
+
+static const char *jbd2_slab_names[JBD2_MAX_SLABS] = {
+       "jbd2_1k", "jbd2_2k", "jbd2_4k", "jbd2_8k",
+       "jbd2_16k", "jbd2_32k", "jbd2_64k", "jbd2_128k"
+};
+
+
+static void jbd2_journal_destroy_slabs(void)
+{
+       int i;
+
+       for (i = 0; i < JBD2_MAX_SLABS; i++) {
+               if (jbd2_slab[i])
+                       kmem_cache_destroy(jbd2_slab[i]);
+               jbd2_slab[i] = NULL;
+       }
+}
+
+static int jbd2_journal_create_slab(size_t size)
+{
+       int i = order_base_2(size) - 10;
+       size_t slab_size;
+
+       if (size == PAGE_SIZE)
+               return 0;
+
+       if (i >= JBD2_MAX_SLABS)
+               return -EINVAL;
+
+       if (unlikely(i < 0))
+               i = 0;
+       down(&jbd2_slab_create_sem);
+       if (jbd2_slab[i]) {
+               up(&jbd2_slab_create_sem);
+               return 0;       /* Already created */
+       }
+
+       slab_size = 1 << (i+10);
+       jbd2_slab[i] = kmem_cache_create(jbd2_slab_names[i], slab_size,
+                                        slab_size, 0, NULL);
+       up(&jbd2_slab_create_sem);
+       if (!jbd2_slab[i]) {
+               printk(KERN_EMERG "JBD2: no memory for jbd2_slab cache\n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static struct kmem_cache *get_slab(size_t size)
+{
+       int i = order_base_2(size) - 10;
+
+       BUG_ON(i >= JBD2_MAX_SLABS);
+       if (unlikely(i < 0))
+               i = 0;
+       BUG_ON(jbd2_slab[i] == 0);
+       return jbd2_slab[i];
+}
+
+void *jbd2_alloc(size_t size, gfp_t flags)
+{
+       void *ptr;
+
+       BUG_ON(size & (size-1)); /* Must be a power of 2 */
+
+       flags |= __GFP_REPEAT;
+       if (size == PAGE_SIZE)
+               ptr = (void *)__get_free_pages(flags, 0);
+       else if (size > PAGE_SIZE) {
+               int order = get_order(size);
+
+               if (order < 3)
+                       ptr = (void *)__get_free_pages(flags, order);
+               else
+                       ptr = vmalloc(size);
+       } else
+               ptr = kmem_cache_alloc(get_slab(size), flags);
+
+       /* Check alignment; SLUB has gotten this wrong in the past,
+        * and this can lead to user data corruption! */
+       BUG_ON(((unsigned long) ptr) & (size-1));
+
+       return ptr;
+}
+
+void jbd2_free(void *ptr, size_t size)
+{
+       if (size == PAGE_SIZE) {
+               free_pages((unsigned long)ptr, 0);
+               return;
+       }
+       if (size > PAGE_SIZE) {
+               int order = get_order(size);
+
+               if (order < 3)
+                       free_pages((unsigned long)ptr, order);
+               else
+                       vfree(ptr);
+               return;
+       }
+       kmem_cache_free(get_slab(size), ptr);
+};
+
 /*
  * Journal_head storage management
  */
@@ -2204,6 +2335,7 @@ static void jbd2_journal_destroy_caches(void)
        jbd2_journal_destroy_revoke_caches();
        jbd2_journal_destroy_jbd2_journal_head_cache();
        jbd2_journal_destroy_handle_cache();
+       jbd2_journal_destroy_slabs();
 }
 
 static int __init journal_init(void)
index a0512700542f3fd93c768dde727e23f77a5e3597..bfc70f57900fddb0d63039fb0a2cd662abf963ff 100644 (file)
@@ -1727,6 +1727,21 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
        if (!jh)
                goto zap_buffer_no_jh;
 
+       /*
+        * We cannot remove the buffer from checkpoint lists until the
+        * transaction adding inode to orphan list (let's call it T)
+        * is committed.  Otherwise if the transaction changing the
+        * buffer would be cleaned from the journal before T is
+        * committed, a crash will cause that the correct contents of
+        * the buffer will be lost.  On the other hand we have to
+        * clear the buffer dirty bit at latest at the moment when the
+        * transaction marking the buffer as freed in the filesystem
+        * structures is committed because from that moment on the
+        * buffer can be reallocated and used by a different page.
+        * Since the block hasn't been freed yet but the inode has
+        * already been added to orphan list, it is safe for us to add
+        * the buffer to BJ_Forget list of the newest transaction.
+        */
        transaction = jh->b_transaction;
        if (transaction == NULL) {
                /* First case: not on any transaction.  If it
@@ -1783,16 +1798,15 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
        } else if (transaction == journal->j_committing_transaction) {
                JBUFFER_TRACE(jh, "on committing transaction");
                /*
-                * If it is committing, we simply cannot touch it.  We
-                * can remove it's next_transaction pointer from the
-                * running transaction if that is set, but nothing
-                * else. */
+                * The buffer is committing, we simply cannot touch
+                * it. So we just set j_next_transaction to the
+                * running transaction (if there is one) and mark
+                * buffer as freed so that commit code knows it should
+                * clear dirty bits when it is done with the buffer.
+                */
                set_buffer_freed(bh);
-               if (jh->b_next_transaction) {
-                       J_ASSERT(jh->b_next_transaction ==
-                                       journal->j_running_transaction);
-                       jh->b_next_transaction = NULL;
-               }
+               if (journal->j_running_transaction && buffer_jbddirty(bh))
+                       jh->b_next_transaction = journal->j_running_transaction;
                jbd2_journal_put_journal_head(jh);
                spin_unlock(&journal->j_list_lock);
                jbd_unlock_bh_state(bh);
@@ -1969,7 +1983,7 @@ void jbd2_journal_file_buffer(struct journal_head *jh,
  */
 void __jbd2_journal_refile_buffer(struct journal_head *jh)
 {
-       int was_dirty;
+       int was_dirty, jlist;
        struct buffer_head *bh = jh2bh(jh);
 
        J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
@@ -1991,8 +2005,13 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
        __jbd2_journal_temp_unlink_buffer(jh);
        jh->b_transaction = jh->b_next_transaction;
        jh->b_next_transaction = NULL;
-       __jbd2_journal_file_buffer(jh, jh->b_transaction,
-                               jh->b_modified ? BJ_Metadata : BJ_Reserved);
+       if (buffer_freed(bh))
+               jlist = BJ_Forget;
+       else if (jh->b_modified)
+               jlist = BJ_Metadata;
+       else
+               jlist = BJ_Reserved;
+       __jbd2_journal_file_buffer(jh, jh->b_transaction, jlist);
        J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
 
        if (was_dirty)
index d66477c34306aae7730cb79ec3793ca220109aea..213169780b6cb7292fda108700056ec02f5758bd 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <linux/sched.h>
 #include <linux/fs.h>
-#include <linux/quotaops.h>
 #include <linux/posix_acl_xattr.h>
 #include "jfs_incore.h"
 #include "jfs_txnmgr.h"
@@ -174,7 +173,7 @@ cleanup:
        return rc;
 }
 
-static int jfs_acl_chmod(struct inode *inode)
+int jfs_acl_chmod(struct inode *inode)
 {
        struct posix_acl *acl, *clone;
        int rc;
@@ -205,26 +204,3 @@ static int jfs_acl_chmod(struct inode *inode)
        posix_acl_release(clone);
        return rc;
 }
-
-int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
-{
-       struct inode *inode = dentry->d_inode;
-       int rc;
-
-       rc = inode_change_ok(inode, iattr);
-       if (rc)
-               return rc;
-
-       if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
-           (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {
-               if (vfs_dq_transfer(inode, iattr))
-                       return -EDQUOT;
-       }
-
-       rc = inode_setattr(inode, iattr);
-
-       if (!rc && (iattr->ia_valid & ATTR_MODE))
-               rc = jfs_acl_chmod(inode);
-
-       return rc;
-}
index 2b70fa78e4a7ab664a1dff1b7c32698b3f269474..14ba982b3f24f96c60f45472749816d17eea0194 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/quotaops.h>
 #include "jfs_incore.h"
 #include "jfs_inode.h"
 #include "jfs_dmap.h"
@@ -47,7 +48,7 @@ static int jfs_open(struct inode *inode, struct file *file)
 {
        int rc;
 
-       if ((rc = generic_file_open(inode, file)))
+       if ((rc = dquot_file_open(inode, file)))
                return rc;
 
        /*
@@ -88,14 +89,40 @@ static int jfs_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+int jfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+       struct inode *inode = dentry->d_inode;
+       int rc;
+
+       rc = inode_change_ok(inode, iattr);
+       if (rc)
+               return rc;
+
+       if (iattr->ia_valid & ATTR_SIZE)
+               dquot_initialize(inode);
+       if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
+           (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {
+               rc = dquot_transfer(inode, iattr);
+               if (rc)
+                       return rc;
+       }
+
+       rc = inode_setattr(inode, iattr);
+
+       if (!rc && (iattr->ia_valid & ATTR_MODE))
+               rc = jfs_acl_chmod(inode);
+
+       return rc;
+}
+
 const struct inode_operations jfs_file_inode_operations = {
        .truncate       = jfs_truncate,
        .setxattr       = jfs_setxattr,
        .getxattr       = jfs_getxattr,
        .listxattr      = jfs_listxattr,
        .removexattr    = jfs_removexattr,
-#ifdef CONFIG_JFS_POSIX_ACL
        .setattr        = jfs_setattr,
+#ifdef CONFIG_JFS_POSIX_ACL
        .check_acl      = jfs_check_acl,
 #endif
 };
index b2ae190a77ba240b04f8175d7b2e2c65aaa20b3d..9dd126276c9f429c2be8990bca4af460d8d2a2af 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/buffer_head.h>
 #include <linux/pagemap.h>
 #include <linux/quotaops.h>
+#include <linux/writeback.h>
 #include "jfs_incore.h"
 #include "jfs_inode.h"
 #include "jfs_filsys.h"
@@ -120,8 +121,10 @@ int jfs_commit_inode(struct inode *inode, int wait)
        return rc;
 }
 
-int jfs_write_inode(struct inode *inode, int wait)
+int jfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
+       int wait = wbc->sync_mode == WB_SYNC_ALL;
+
        if (test_cflag(COMMIT_Nolink, inode))
                return 0;
        /*
@@ -146,6 +149,9 @@ void jfs_delete_inode(struct inode *inode)
 {
        jfs_info("In jfs_delete_inode, inode = 0x%p", inode);
 
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
+
        if (!is_bad_inode(inode) &&
            (JFS_IP(inode)->fileset == FILESYSTEM_I)) {
                truncate_inode_pages(&inode->i_data, 0);
@@ -158,9 +164,9 @@ void jfs_delete_inode(struct inode *inode)
                /*
                 * Free the inode from the quota allocation.
                 */
-               vfs_dq_init(inode);
-               vfs_dq_free_inode(inode);
-               vfs_dq_drop(inode);
+               dquot_initialize(inode);
+               dquot_free_inode(inode);
+               dquot_drop(inode);
        }
 
        clear_inode(inode);
index b07bd417ef85f66a53bcd13425c4548d660f6c6d..54e07559878d12e2fe77c7b39e4f2114c7a3abdb 100644 (file)
@@ -22,7 +22,7 @@
 
 int jfs_check_acl(struct inode *, int);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
-int jfs_setattr(struct dentry *, struct iattr *);
+int jfs_acl_chmod(struct inode *inode);
 
 #else
 
@@ -32,5 +32,10 @@ static inline int jfs_init_acl(tid_t tid, struct inode *inode,
        return 0;
 }
 
+static inline int jfs_acl_chmod(struct inode *inode)
+{
+       return 0;
+}
+
 #endif
 #endif         /* _H_JFS_ACL */
index 925871e9887baf83f8825cf9d6b04e297f945cbb..0e4623be70ceb6dcee419d1c392c8e92cf1bfac3 100644 (file)
@@ -381,10 +381,10 @@ static u32 add_index(tid_t tid, struct inode *ip, s64 bn, int slot)
                 * It's time to move the inline table to an external
                 * page and begin to build the xtree
                 */
-               if (vfs_dq_alloc_block(ip, sbi->nbperpage))
+               if (dquot_alloc_block(ip, sbi->nbperpage))
                        goto clean_up;
                if (dbAlloc(ip, 0, sbi->nbperpage, &xaddr)) {
-                       vfs_dq_free_block(ip, sbi->nbperpage);
+                       dquot_free_block(ip, sbi->nbperpage);
                        goto clean_up;
                }
 
@@ -408,7 +408,7 @@ static u32 add_index(tid_t tid, struct inode *ip, s64 bn, int slot)
                        memcpy(&jfs_ip->i_dirtable, temp_table,
                               sizeof (temp_table));
                        dbFree(ip, xaddr, sbi->nbperpage);
-                       vfs_dq_free_block(ip, sbi->nbperpage);
+                       dquot_free_block(ip, sbi->nbperpage);
                        goto clean_up;
                }
                ip->i_size = PSIZE;
@@ -1027,10 +1027,9 @@ static int dtSplitUp(tid_t tid,
                        n = xlen;
 
                /* Allocate blocks to quota. */
-               if (vfs_dq_alloc_block(ip, n)) {
-                       rc = -EDQUOT;
+               rc = dquot_alloc_block(ip, n);
+               if (rc)
                        goto extendOut;
-               }
                quota_allocation += n;
 
                if ((rc = dbReAlloc(sbi->ipbmap, xaddr, (s64) xlen,
@@ -1308,7 +1307,7 @@ static int dtSplitUp(tid_t tid,
 
        /* Rollback quota allocation */
        if (rc && quota_allocation)
-               vfs_dq_free_block(ip, quota_allocation);
+               dquot_free_block(ip, quota_allocation);
 
       dtSplitUp_Exit:
 
@@ -1369,9 +1368,10 @@ static int dtSplitPage(tid_t tid, struct inode *ip, struct dtsplit * split,
                return -EIO;
 
        /* Allocate blocks to quota. */
-       if (vfs_dq_alloc_block(ip, lengthPXD(pxd))) {
+       rc = dquot_alloc_block(ip, lengthPXD(pxd));
+       if (rc) {
                release_metapage(rmp);
-               return -EDQUOT;
+               return rc;
        }
 
        jfs_info("dtSplitPage: ip:0x%p smp:0x%p rmp:0x%p", ip, smp, rmp);
@@ -1892,6 +1892,7 @@ static int dtSplitRoot(tid_t tid,
        struct dt_lock *dtlck;
        struct tlock *tlck;
        struct lv *lv;
+       int rc;
 
        /* get split root page */
        smp = split->mp;
@@ -1916,9 +1917,10 @@ static int dtSplitRoot(tid_t tid,
        rp = rmp->data;
 
        /* Allocate blocks to quota. */
-       if (vfs_dq_alloc_block(ip, lengthPXD(pxd))) {
+       rc = dquot_alloc_block(ip, lengthPXD(pxd));
+       if (rc) {
                release_metapage(rmp);
-               return -EDQUOT;
+               return rc;
        }
 
        BT_MARK_DIRTY(rmp, ip);
@@ -2287,7 +2289,7 @@ static int dtDeleteUp(tid_t tid, struct inode *ip,
        xlen = lengthPXD(&fp->header.self);
 
        /* Free quota allocation. */
-       vfs_dq_free_block(ip, xlen);
+       dquot_free_block(ip, xlen);
 
        /* free/invalidate its buffer page */
        discard_metapage(fmp);
@@ -2363,7 +2365,7 @@ static int dtDeleteUp(tid_t tid, struct inode *ip,
                                xlen = lengthPXD(&p->header.self);
 
                                /* Free quota allocation */
-                               vfs_dq_free_block(ip, xlen);
+                               dquot_free_block(ip, xlen);
 
                                /* free/invalidate its buffer page */
                                discard_metapage(mp);
index 41d6045dbeb08b5db952e9945a054bf722470451..5d3bbd10f8db11784ed2ed18e34e13ec64c57551 100644 (file)
@@ -141,10 +141,11 @@ extAlloc(struct inode *ip, s64 xlen, s64 pno, xad_t * xp, bool abnr)
        }
 
        /* Allocate blocks to quota. */
-       if (vfs_dq_alloc_block(ip, nxlen)) {
+       rc = dquot_alloc_block(ip, nxlen);
+       if (rc) {
                dbFree(ip, nxaddr, (s64) nxlen);
                mutex_unlock(&JFS_IP(ip)->commit_mutex);
-               return -EDQUOT;
+               return rc;
        }
 
        /* determine the value of the extent flag */
@@ -164,7 +165,7 @@ extAlloc(struct inode *ip, s64 xlen, s64 pno, xad_t * xp, bool abnr)
         */
        if (rc) {
                dbFree(ip, nxaddr, nxlen);
-               vfs_dq_free_block(ip, nxlen);
+               dquot_free_block(ip, nxlen);
                mutex_unlock(&JFS_IP(ip)->commit_mutex);
                return (rc);
        }
@@ -256,10 +257,11 @@ int extRealloc(struct inode *ip, s64 nxlen, xad_t * xp, bool abnr)
                goto exit;
 
        /* Allocat blocks to quota. */
-       if (vfs_dq_alloc_block(ip, nxlen)) {
+       rc = dquot_alloc_block(ip, nxlen);
+       if (rc) {
                dbFree(ip, nxaddr, (s64) nxlen);
                mutex_unlock(&JFS_IP(ip)->commit_mutex);
-               return -EDQUOT;
+               return rc;
        }
 
        delta = nxlen - xlen;
@@ -297,7 +299,7 @@ int extRealloc(struct inode *ip, s64 nxlen, xad_t * xp, bool abnr)
                /* extend the extent */
                if ((rc = xtExtend(0, ip, xoff + xlen, (int) nextend, 0))) {
                        dbFree(ip, xaddr + xlen, delta);
-                       vfs_dq_free_block(ip, nxlen);
+                       dquot_free_block(ip, nxlen);
                        goto exit;
                }
        } else {
@@ -308,7 +310,7 @@ int extRealloc(struct inode *ip, s64 nxlen, xad_t * xp, bool abnr)
                 */
                if ((rc = xtTailgate(0, ip, xoff, (int) ntail, nxaddr, 0))) {
                        dbFree(ip, nxaddr, nxlen);
-                       vfs_dq_free_block(ip, nxlen);
+                       dquot_free_block(ip, nxlen);
                        goto exit;
                }
        }
index dc0e02159ac9e494fc99a33dcfae47ca3f5a4f5f..829921b677651b166c5d788ddff640d14d63944c 100644 (file)
@@ -116,10 +116,10 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
        /*
         * Allocate inode to quota.
         */
-       if (vfs_dq_alloc_inode(inode)) {
-               rc = -EDQUOT;
+       dquot_initialize(inode);
+       rc = dquot_alloc_inode(inode);
+       if (rc)
                goto fail_drop;
-       }
 
        inode->i_mode = mode;
        /* inherit flags from parent */
@@ -162,7 +162,7 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
        return inode;
 
 fail_drop:
-       vfs_dq_drop(inode);
+       dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
 fail_unlock:
        inode->i_nlink = 0;
index 1eff7db34d6357a611dbbfbff5bd64c78cac42ed..79e2c79661dfdf897ecac4832f55c91164c24a14 100644 (file)
@@ -26,7 +26,7 @@ extern long jfs_ioctl(struct file *, unsigned int, unsigned long);
 extern long jfs_compat_ioctl(struct file *, unsigned int, unsigned long);
 extern struct inode *jfs_iget(struct super_block *, unsigned long);
 extern int jfs_commit_inode(struct inode *, int);
-extern int jfs_write_inode(struct inode*, int);
+extern int jfs_write_inode(struct inode *, struct writeback_control *);
 extern void jfs_delete_inode(struct inode *);
 extern void jfs_dirty_inode(struct inode *);
 extern void jfs_truncate(struct inode *);
@@ -40,6 +40,7 @@ extern struct dentry *jfs_fh_to_parent(struct super_block *sb, struct fid *fid,
        int fh_len, int fh_type);
 extern void jfs_set_inode_flags(struct inode *);
 extern int jfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
+extern int jfs_setattr(struct dentry *, struct iattr *);
 
 extern const struct address_space_operations jfs_aops;
 extern const struct inode_operations jfs_dir_inode_operations;
index d654a6458648d1a3427be4924f5463f5d77d5f40..6c50871e62203d3bd9fd81d7c5cde22cfd3f75b4 100644 (file)
@@ -585,10 +585,10 @@ int xtInsert(tid_t tid,           /* transaction id */
                        hint = addressXAD(xad) + lengthXAD(xad) - 1;
                } else
                        hint = 0;
-               if ((rc = vfs_dq_alloc_block(ip, xlen)))
+               if ((rc = dquot_alloc_block(ip, xlen)))
                        goto out;
                if ((rc = dbAlloc(ip, hint, (s64) xlen, &xaddr))) {
-                       vfs_dq_free_block(ip, xlen);
+                       dquot_free_block(ip, xlen);
                        goto out;
                }
        }
@@ -617,7 +617,7 @@ int xtInsert(tid_t tid,             /* transaction id */
                        /* undo data extent allocation */
                        if (*xaddrp == 0) {
                                dbFree(ip, xaddr, (s64) xlen);
-                               vfs_dq_free_block(ip, xlen);
+                               dquot_free_block(ip, xlen);
                        }
                        return rc;
                }
@@ -985,10 +985,9 @@ xtSplitPage(tid_t tid, struct inode *ip,
        rbn = addressPXD(pxd);
 
        /* Allocate blocks to quota. */
-       if (vfs_dq_alloc_block(ip, lengthPXD(pxd))) {
-               rc = -EDQUOT;
+       rc = dquot_alloc_block(ip, lengthPXD(pxd));
+       if (rc)
                goto clean_up;
-       }
 
        quota_allocation += lengthPXD(pxd);
 
@@ -1195,7 +1194,7 @@ xtSplitPage(tid_t tid, struct inode *ip,
 
        /* Rollback quota allocation. */
        if (quota_allocation)
-               vfs_dq_free_block(ip, quota_allocation);
+               dquot_free_block(ip, quota_allocation);
 
        return (rc);
 }
@@ -1235,6 +1234,7 @@ xtSplitRoot(tid_t tid,
        struct pxdlist *pxdlist;
        struct tlock *tlck;
        struct xtlock *xtlck;
+       int rc;
 
        sp = &JFS_IP(ip)->i_xtroot;
 
@@ -1252,9 +1252,10 @@ xtSplitRoot(tid_t tid,
                return -EIO;
 
        /* Allocate blocks to quota. */
-       if (vfs_dq_alloc_block(ip, lengthPXD(pxd))) {
+       rc = dquot_alloc_block(ip, lengthPXD(pxd));
+       if (rc) {
                release_metapage(rmp);
-               return -EDQUOT;
+               return rc;
        }
 
        jfs_info("xtSplitRoot: ip:0x%p rmp:0x%p", ip, rmp);
@@ -3680,7 +3681,7 @@ s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int flag)
                ip->i_size = newsize;
 
        /* update quota allocation to reflect freed blocks */
-       vfs_dq_free_block(ip, nfreed);
+       dquot_free_block(ip, nfreed);
 
        /*
         * free tlock of invalidated pages
index c79a4270f0837a42e4f52d7e3eacf56dabe9745f..4a3e9f39c21d05a430dba68a87e9a1fb0a73428b 100644 (file)
@@ -85,6 +85,8 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode,
 
        jfs_info("jfs_create: dip:0x%p name:%s", dip, dentry->d_name.name);
 
+       dquot_initialize(dip);
+
        /*
         * search parent directory for entry/freespace
         * (dtSearch() returns parent directory page pinned)
@@ -215,6 +217,8 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
 
        jfs_info("jfs_mkdir: dip:0x%p name:%s", dip, dentry->d_name.name);
 
+       dquot_initialize(dip);
+
        /* link count overflow on parent directory ? */
        if (dip->i_nlink == JFS_LINK_MAX) {
                rc = -EMLINK;
@@ -356,7 +360,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
        jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name);
 
        /* Init inode for quota operations. */
-       vfs_dq_init(ip);
+       dquot_initialize(dip);
+       dquot_initialize(ip);
 
        /* directory must be empty to be removed */
        if (!dtEmpty(ip)) {
@@ -483,7 +488,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry)
        jfs_info("jfs_unlink: dip:0x%p name:%s", dip, dentry->d_name.name);
 
        /* Init inode for quota operations. */
-       vfs_dq_init(ip);
+       dquot_initialize(dip);
+       dquot_initialize(ip);
 
        if ((rc = get_UCSname(&dname, dentry)))
                goto out;
@@ -805,6 +811,8 @@ static int jfs_link(struct dentry *old_dentry,
        if (ip->i_nlink == 0)
                return -ENOENT;
 
+       dquot_initialize(dir);
+
        tid = txBegin(ip->i_sb, 0);
 
        mutex_lock_nested(&JFS_IP(dir)->commit_mutex, COMMIT_MUTEX_PARENT);
@@ -896,6 +904,8 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
 
        jfs_info("jfs_symlink: dip:0x%p name:%s", dip, name);
 
+       dquot_initialize(dip);
+
        ssize = strlen(name) + 1;
 
        /*
@@ -1087,6 +1097,9 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        jfs_info("jfs_rename: %s %s", old_dentry->d_name.name,
                 new_dentry->d_name.name);
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        old_ip = old_dentry->d_inode;
        new_ip = new_dentry->d_inode;
 
@@ -1136,7 +1149,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        } else if (new_ip) {
                IWRITE_LOCK(new_ip, RDWRLOCK_NORMAL);
                /* Init inode for quota operations. */
-               vfs_dq_init(new_ip);
+               dquot_initialize(new_ip);
        }
 
        /*
@@ -1360,6 +1373,8 @@ static int jfs_mknod(struct inode *dir, struct dentry *dentry,
 
        jfs_info("jfs_mknod: %s", dentry->d_name.name);
 
+       dquot_initialize(dir);
+
        if ((rc = get_UCSname(&dname, dentry)))
                goto out;
 
@@ -1541,8 +1556,8 @@ const struct inode_operations jfs_dir_inode_operations = {
        .getxattr       = jfs_getxattr,
        .listxattr      = jfs_listxattr,
        .removexattr    = jfs_removexattr,
-#ifdef CONFIG_JFS_POSIX_ACL
        .setattr        = jfs_setattr,
+#ifdef CONFIG_JFS_POSIX_ACL
        .check_acl      = jfs_check_acl,
 #endif
 };
index d929a822a74e6410fd89727f4351e164f8e65ede..266699deb1c68825d29eee529d0109dcf9ce50a0 100644 (file)
@@ -131,6 +131,11 @@ static void jfs_destroy_inode(struct inode *inode)
        kmem_cache_free(jfs_inode_cachep, ji);
 }
 
+static void jfs_clear_inode(struct inode *inode)
+{
+       dquot_drop(inode);
+}
+
 static int jfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct jfs_sb_info *sbi = JFS_SBI(dentry->d_sb);
@@ -745,6 +750,7 @@ static const struct super_operations jfs_super_operations = {
        .dirty_inode    = jfs_dirty_inode,
        .write_inode    = jfs_write_inode,
        .delete_inode   = jfs_delete_inode,
+       .clear_inode    = jfs_clear_inode,
        .put_super      = jfs_put_super,
        .sync_fs        = jfs_sync_fs,
        .freeze_fs      = jfs_freeze,
index fad364548bc9e3716b08dc0f6ca66a58d1f9b56c..1f594ab21895cb28c81f3b0238a434147d485198 100644 (file)
@@ -260,14 +260,14 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size,
        nblocks = (size + (sb->s_blocksize - 1)) >> sb->s_blocksize_bits;
 
        /* Allocate new blocks to quota. */
-       if (vfs_dq_alloc_block(ip, nblocks)) {
-               return -EDQUOT;
-       }
+       rc = dquot_alloc_block(ip, nblocks);
+       if (rc)
+               return rc;
 
        rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno);
        if (rc) {
                /*Rollback quota allocation. */
-               vfs_dq_free_block(ip, nblocks);
+               dquot_free_block(ip, nblocks);
                return rc;
        }
 
@@ -332,7 +332,7 @@ static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size,
 
       failed:
        /* Rollback quota allocation. */
-       vfs_dq_free_block(ip, nblocks);
+       dquot_free_block(ip, nblocks);
 
        dbFree(ip, blkno, nblocks);
        return rc;
@@ -538,7 +538,8 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
 
        if (blocks_needed > current_blocks) {
                /* Allocate new blocks to quota. */
-               if (vfs_dq_alloc_block(inode, blocks_needed))
+               rc = dquot_alloc_block(inode, blocks_needed);
+               if (rc)
                        return -EDQUOT;
 
                quota_allocation = blocks_needed;
@@ -602,7 +603,7 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
       clean_up:
        /* Rollback quota allocation */
        if (quota_allocation)
-               vfs_dq_free_block(inode, quota_allocation);
+               dquot_free_block(inode, quota_allocation);
 
        return (rc);
 }
@@ -677,7 +678,7 @@ static int ea_put(tid_t tid, struct inode *inode, struct ea_buffer *ea_buf,
 
        /* If old blocks exist, they must be removed from quota allocation. */
        if (old_blocks)
-               vfs_dq_free_block(inode, old_blocks);
+               dquot_free_block(inode, old_blocks);
 
        inode->i_ctime = CURRENT_TIME;
 
index 6e8d17e1dc4c95db609308d2ef59c8e314be79fe..9e50bcf55857eaebd8fc4106f21bae8ad69afbf1 100644 (file)
@@ -338,28 +338,14 @@ int simple_readpage(struct file *file, struct page *page)
        return 0;
 }
 
-int simple_prepare_write(struct file *file, struct page *page,
-                       unsigned from, unsigned to)
-{
-       if (!PageUptodate(page)) {
-               if (to - from != PAGE_CACHE_SIZE)
-                       zero_user_segments(page,
-                               0, from,
-                               to, PAGE_CACHE_SIZE);
-       }
-       return 0;
-}
-
 int simple_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata)
 {
        struct page *page;
        pgoff_t index;
-       unsigned from;
 
        index = pos >> PAGE_CACHE_SHIFT;
-       from = pos & (PAGE_CACHE_SIZE - 1);
 
        page = grab_cache_page_write_begin(mapping, index, flags);
        if (!page)
@@ -367,43 +353,59 @@ int simple_write_begin(struct file *file, struct address_space *mapping,
 
        *pagep = page;
 
-       return simple_prepare_write(file, page, from, from+len);
-}
-
-static int simple_commit_write(struct file *file, struct page *page,
-                              unsigned from, unsigned to)
-{
-       struct inode *inode = page->mapping->host;
-       loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+       if (!PageUptodate(page) && (len != PAGE_CACHE_SIZE)) {
+               unsigned from = pos & (PAGE_CACHE_SIZE - 1);
 
-       if (!PageUptodate(page))
-               SetPageUptodate(page);
-       /*
-        * No need to use i_size_read() here, the i_size
-        * cannot change under us because we hold the i_mutex.
-        */
-       if (pos > inode->i_size)
-               i_size_write(inode, pos);
-       set_page_dirty(page);
+               zero_user_segments(page, 0, from, from + len, PAGE_CACHE_SIZE);
+       }
        return 0;
 }
 
+/**
+ * simple_write_end - .write_end helper for non-block-device FSes
+ * @available: See .write_end of address_space_operations
+ * @file:              "
+ * @mapping:           "
+ * @pos:               "
+ * @len:               "
+ * @copied:            "
+ * @page:              "
+ * @fsdata:            "
+ *
+ * simple_write_end does the minimum needed for updating a page after writing is
+ * done. It has the same API signature as the .write_end of
+ * address_space_operations vector. So it can just be set onto .write_end for
+ * FSes that don't need any other processing. i_mutex is assumed to be held.
+ * Block based filesystems should use generic_write_end().
+ * NOTE: Even though i_size might get updated by this function, mark_inode_dirty
+ * is not called, so a filesystem that actually does store data in .write_inode
+ * should extend on what's done here with a call to mark_inode_dirty() in the
+ * case that i_size has changed.
+ */
 int simple_write_end(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned copied,
                        struct page *page, void *fsdata)
 {
-       unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+       struct inode *inode = page->mapping->host;
+       loff_t last_pos = pos + copied;
 
        /* zero the stale part of the page if we did a short copy */
        if (copied < len) {
-               void *kaddr = kmap_atomic(page, KM_USER0);
-               memset(kaddr + from + copied, 0, len - copied);
-               flush_dcache_page(page);
-               kunmap_atomic(kaddr, KM_USER0);
+               unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+
+               zero_user(page, from + copied, len - copied);
        }
 
-       simple_commit_write(file, page, from, from+copied);
+       if (!PageUptodate(page))
+               SetPageUptodate(page);
+       /*
+        * No need to use i_size_read() here, the i_size
+        * cannot change under us because we hold the i_mutex.
+        */
+       if (last_pos > inode->i_size)
+               i_size_write(inode, last_pos);
 
+       set_page_dirty(page);
        unlock_page(page);
        page_cache_release(page);
 
@@ -853,7 +855,6 @@ EXPORT_SYMBOL(simple_getattr);
 EXPORT_SYMBOL(simple_link);
 EXPORT_SYMBOL(simple_lookup);
 EXPORT_SYMBOL(simple_pin_fs);
-EXPORT_UNUSED_SYMBOL(simple_prepare_write);
 EXPORT_SYMBOL(simple_readpage);
 EXPORT_SYMBOL(simple_release_fs);
 EXPORT_SYMBOL(simple_rename);
index 4600c2037b8bb8dd71dee46c2cac3cec5cfce296..bb464d12104c44dd67b0eee1496c49a8df138ac7 100644 (file)
@@ -479,8 +479,8 @@ again:      mutex_lock(&nlm_host_mutex);
                        }
                }
        }
-
        mutex_unlock(&nlm_host_mutex);
+       nsm_release(nsm);
 }
 
 /*
index f956651d0f651a66a00d4bb9a1d05febf761d524..fefa4df3f005bd9cdf0e1a10b0ab0bfce72f976e 100644 (file)
@@ -349,9 +349,9 @@ retry:
  * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
  * @info: pointer to NLMPROC_SM_NOTIFY arguments
  *
- * Returns a matching nsm_handle if found in the nsm cache; the returned
- * nsm_handle's reference count is bumped and sm_monitored is cleared.
- * Otherwise returns NULL if some error occurred.
+ * Returns a matching nsm_handle if found in the nsm cache. The returned
+ * nsm_handle's reference count is bumped. Otherwise returns NULL if some
+ * error occurred.
  */
 struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info)
 {
@@ -370,12 +370,6 @@ struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info)
        atomic_inc(&cached->sm_count);
        spin_unlock(&nsm_lock);
 
-       /*
-        * During subsequent lock activity, force a fresh
-        * notification to be set up for this host.
-        */
-       cached->sm_monitored = 0;
-
        dprintk("lockd: host %s (%s) rebooted, cnt %d\n",
                        cached->sm_name, cached->sm_addrbuf,
                        atomic_read(&cached->sm_count));
index e50cfa3d965457f4f07a9e038d1f03540d39d69d..7d150517ddf0445943ed58e2625494deef712d4e 100644 (file)
@@ -243,11 +243,9 @@ static int make_socks(struct svc_serv *serv)
        if (err < 0)
                goto out_err;
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        err = create_lockd_family(serv, PF_INET6);
        if (err < 0 && err != -EAFNOSUPPORT)
                goto out_err;
-#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
 
        warned = 0;
        return 0;
index a8794f233bc9c38d45c47f69d03652f7f6f8d533..ae9ded026b7cb18a783af01bffac31dd87d6a26e 100644 (file)
@@ -1182,8 +1182,9 @@ int __break_lease(struct inode *inode, unsigned int mode)
        struct file_lock *fl;
        unsigned long break_time;
        int i_have_this_lease = 0;
+       int want_write = (mode & O_ACCMODE) != O_RDONLY;
 
-       new_fl = lease_alloc(NULL, mode & FMODE_WRITE ? F_WRLCK : F_RDLCK);
+       new_fl = lease_alloc(NULL, want_write ? F_WRLCK : F_RDLCK);
 
        lock_kernel();
 
@@ -1197,7 +1198,7 @@ int __break_lease(struct inode *inode, unsigned int mode)
                if (fl->fl_owner == current->files)
                        i_have_this_lease = 1;
 
-       if (mode & FMODE_WRITE) {
+       if (want_write) {
                /* If we want write access, we have to revoke any lease. */
                future = F_UNLCK | F_INPROGRESS;
        } else if (flock->fl_type & F_INPROGRESS) {
diff --git a/fs/logfs/Kconfig b/fs/logfs/Kconfig
new file mode 100644 (file)
index 0000000..daf9a9b
--- /dev/null
@@ -0,0 +1,17 @@
+config LOGFS
+       tristate "LogFS file system (EXPERIMENTAL)"
+       depends on (MTD || BLOCK) && EXPERIMENTAL
+       select ZLIB_INFLATE
+       select ZLIB_DEFLATE
+       select CRC32
+       select BTREE
+       help
+         Flash filesystem aimed to scale efficiently to large devices.
+         In comparison to JFFS2 it offers significantly faster mount
+         times and potentially less RAM usage, although the latter has
+         not been measured yet.
+
+         In its current state it is still very experimental and should
+         not be used for other than testing purposes.
+
+         If unsure, say N.
diff --git a/fs/logfs/Makefile b/fs/logfs/Makefile
new file mode 100644 (file)
index 0000000..4820027
--- /dev/null
@@ -0,0 +1,13 @@
+obj-$(CONFIG_LOGFS)    += logfs.o
+
+logfs-y        += compr.o
+logfs-y        += dir.o
+logfs-y        += file.o
+logfs-y        += gc.o
+logfs-y        += inode.o
+logfs-y        += journal.o
+logfs-y        += readwrite.o
+logfs-y        += segment.o
+logfs-y        += super.o
+logfs-$(CONFIG_BLOCK)  += dev_bdev.o
+logfs-$(CONFIG_MTD)    += dev_mtd.o
diff --git a/fs/logfs/compr.c b/fs/logfs/compr.c
new file mode 100644 (file)
index 0000000..44bbfd2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * fs/logfs/compr.c    - compression routines
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+#include <linux/vmalloc.h>
+#include <linux/zlib.h>
+
+#define COMPR_LEVEL 3
+
+static DEFINE_MUTEX(compr_mutex);
+static struct z_stream_s stream;
+
+int logfs_compress(void *in, void *out, size_t inlen, size_t outlen)
+{
+       int err, ret;
+
+       ret = -EIO;
+       mutex_lock(&compr_mutex);
+       err = zlib_deflateInit(&stream, COMPR_LEVEL);
+       if (err != Z_OK)
+               goto error;
+
+       stream.next_in = in;
+       stream.avail_in = inlen;
+       stream.total_in = 0;
+       stream.next_out = out;
+       stream.avail_out = outlen;
+       stream.total_out = 0;
+
+       err = zlib_deflate(&stream, Z_FINISH);
+       if (err != Z_STREAM_END)
+               goto error;
+
+       err = zlib_deflateEnd(&stream);
+       if (err != Z_OK)
+               goto error;
+
+       if (stream.total_out >= stream.total_in)
+               goto error;
+
+       ret = stream.total_out;
+error:
+       mutex_unlock(&compr_mutex);
+       return ret;
+}
+
+int logfs_uncompress(void *in, void *out, size_t inlen, size_t outlen)
+{
+       int err, ret;
+
+       ret = -EIO;
+       mutex_lock(&compr_mutex);
+       err = zlib_inflateInit(&stream);
+       if (err != Z_OK)
+               goto error;
+
+       stream.next_in = in;
+       stream.avail_in = inlen;
+       stream.total_in = 0;
+       stream.next_out = out;
+       stream.avail_out = outlen;
+       stream.total_out = 0;
+
+       err = zlib_inflate(&stream, Z_FINISH);
+       if (err != Z_STREAM_END)
+               goto error;
+
+       err = zlib_inflateEnd(&stream);
+       if (err != Z_OK)
+               goto error;
+
+       ret = 0;
+error:
+       mutex_unlock(&compr_mutex);
+       return ret;
+}
+
+int __init logfs_compr_init(void)
+{
+       size_t size = max(zlib_deflate_workspacesize(),
+                       zlib_inflate_workspacesize());
+       stream.workspace = vmalloc(size);
+       if (!stream.workspace)
+               return -ENOMEM;
+       return 0;
+}
+
+void logfs_compr_exit(void)
+{
+       vfree(stream.workspace);
+}
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c
new file mode 100644 (file)
index 0000000..9718c22
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * fs/logfs/dev_bdev.c - Device access methods for block devices
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h>
+
+#define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1))
+
+static void request_complete(struct bio *bio, int err)
+{
+       complete((struct completion *)bio->bi_private);
+}
+
+static int sync_request(struct page *page, struct block_device *bdev, int rw)
+{
+       struct bio bio;
+       struct bio_vec bio_vec;
+       struct completion complete;
+
+       bio_init(&bio);
+       bio.bi_io_vec = &bio_vec;
+       bio_vec.bv_page = page;
+       bio_vec.bv_len = PAGE_SIZE;
+       bio_vec.bv_offset = 0;
+       bio.bi_vcnt = 1;
+       bio.bi_idx = 0;
+       bio.bi_size = PAGE_SIZE;
+       bio.bi_bdev = bdev;
+       bio.bi_sector = page->index * (PAGE_SIZE >> 9);
+       init_completion(&complete);
+       bio.bi_private = &complete;
+       bio.bi_end_io = request_complete;
+
+       submit_bio(rw, &bio);
+       generic_unplug_device(bdev_get_queue(bdev));
+       wait_for_completion(&complete);
+       return test_bit(BIO_UPTODATE, &bio.bi_flags) ? 0 : -EIO;
+}
+
+static int bdev_readpage(void *_sb, struct page *page)
+{
+       struct super_block *sb = _sb;
+       struct block_device *bdev = logfs_super(sb)->s_bdev;
+       int err;
+
+       err = sync_request(page, bdev, READ);
+       if (err) {
+               ClearPageUptodate(page);
+               SetPageError(page);
+       } else {
+               SetPageUptodate(page);
+               ClearPageError(page);
+       }
+       unlock_page(page);
+       return err;
+}
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+
+static void writeseg_end_io(struct bio *bio, int err)
+{
+       const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+       struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
+       struct super_block *sb = bio->bi_private;
+       struct logfs_super *super = logfs_super(sb);
+       struct page *page;
+
+       BUG_ON(!uptodate); /* FIXME: Retry io or write elsewhere */
+       BUG_ON(err);
+       BUG_ON(bio->bi_vcnt == 0);
+       do {
+               page = bvec->bv_page;
+               if (--bvec >= bio->bi_io_vec)
+                       prefetchw(&bvec->bv_page->flags);
+
+               end_page_writeback(page);
+       } while (bvec >= bio->bi_io_vec);
+       bio_put(bio);
+       if (atomic_dec_and_test(&super->s_pending_writes))
+               wake_up(&wq);
+}
+
+static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
+               size_t nr_pages)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       struct bio *bio;
+       struct page *page;
+       struct request_queue *q = bdev_get_queue(sb->s_bdev);
+       unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
+       int i;
+
+       bio = bio_alloc(GFP_NOFS, max_pages);
+       BUG_ON(!bio); /* FIXME: handle this */
+
+       for (i = 0; i < nr_pages; i++) {
+               if (i >= max_pages) {
+                       /* Block layer cannot split bios :( */
+                       bio->bi_vcnt = i;
+                       bio->bi_idx = 0;
+                       bio->bi_size = i * PAGE_SIZE;
+                       bio->bi_bdev = super->s_bdev;
+                       bio->bi_sector = ofs >> 9;
+                       bio->bi_private = sb;
+                       bio->bi_end_io = writeseg_end_io;
+                       atomic_inc(&super->s_pending_writes);
+                       submit_bio(WRITE, bio);
+
+                       ofs += i * PAGE_SIZE;
+                       index += i;
+                       nr_pages -= i;
+                       i = 0;
+
+                       bio = bio_alloc(GFP_NOFS, max_pages);
+                       BUG_ON(!bio);
+               }
+               page = find_lock_page(mapping, index + i);
+               BUG_ON(!page);
+               bio->bi_io_vec[i].bv_page = page;
+               bio->bi_io_vec[i].bv_len = PAGE_SIZE;
+               bio->bi_io_vec[i].bv_offset = 0;
+
+               BUG_ON(PageWriteback(page));
+               set_page_writeback(page);
+               unlock_page(page);
+       }
+       bio->bi_vcnt = nr_pages;
+       bio->bi_idx = 0;
+       bio->bi_size = nr_pages * PAGE_SIZE;
+       bio->bi_bdev = super->s_bdev;
+       bio->bi_sector = ofs >> 9;
+       bio->bi_private = sb;
+       bio->bi_end_io = writeseg_end_io;
+       atomic_inc(&super->s_pending_writes);
+       submit_bio(WRITE, bio);
+       return 0;
+}
+
+static void bdev_writeseg(struct super_block *sb, u64 ofs, size_t len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int head;
+
+       BUG_ON(super->s_flags & LOGFS_SB_FLAG_RO);
+
+       if (len == 0) {
+               /* This can happen when the object fit perfectly into a
+                * segment, the segment gets written per sync and subsequently
+                * closed.
+                */
+               return;
+       }
+       head = ofs & (PAGE_SIZE - 1);
+       if (head) {
+               ofs -= head;
+               len += head;
+       }
+       len = PAGE_ALIGN(len);
+       __bdev_writeseg(sb, ofs, ofs >> PAGE_SHIFT, len >> PAGE_SHIFT);
+       generic_unplug_device(bdev_get_queue(logfs_super(sb)->s_bdev));
+}
+
+
+static void erase_end_io(struct bio *bio, int err) 
+{ 
+       const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); 
+       struct super_block *sb = bio->bi_private; 
+       struct logfs_super *super = logfs_super(sb); 
+
+       BUG_ON(!uptodate); /* FIXME: Retry io or write elsewhere */ 
+       BUG_ON(err); 
+       BUG_ON(bio->bi_vcnt == 0); 
+       bio_put(bio); 
+       if (atomic_dec_and_test(&super->s_pending_writes))
+               wake_up(&wq); 
+} 
+
+static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
+               size_t nr_pages)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct bio *bio;
+       struct request_queue *q = bdev_get_queue(sb->s_bdev);
+       unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
+       int i;
+
+       bio = bio_alloc(GFP_NOFS, max_pages);
+       BUG_ON(!bio); /* FIXME: handle this */
+
+       for (i = 0; i < nr_pages; i++) {
+               if (i >= max_pages) {
+                       /* Block layer cannot split bios :( */
+                       bio->bi_vcnt = i;
+                       bio->bi_idx = 0;
+                       bio->bi_size = i * PAGE_SIZE;
+                       bio->bi_bdev = super->s_bdev;
+                       bio->bi_sector = ofs >> 9;
+                       bio->bi_private = sb;
+                       bio->bi_end_io = erase_end_io;
+                       atomic_inc(&super->s_pending_writes);
+                       submit_bio(WRITE, bio);
+
+                       ofs += i * PAGE_SIZE;
+                       index += i;
+                       nr_pages -= i;
+                       i = 0;
+
+                       bio = bio_alloc(GFP_NOFS, max_pages);
+                       BUG_ON(!bio);
+               }
+               bio->bi_io_vec[i].bv_page = super->s_erase_page;
+               bio->bi_io_vec[i].bv_len = PAGE_SIZE;
+               bio->bi_io_vec[i].bv_offset = 0;
+       }
+       bio->bi_vcnt = nr_pages;
+       bio->bi_idx = 0;
+       bio->bi_size = nr_pages * PAGE_SIZE;
+       bio->bi_bdev = super->s_bdev;
+       bio->bi_sector = ofs >> 9;
+       bio->bi_private = sb;
+       bio->bi_end_io = erase_end_io;
+       atomic_inc(&super->s_pending_writes);
+       submit_bio(WRITE, bio);
+       return 0;
+}
+
+static int bdev_erase(struct super_block *sb, loff_t to, size_t len,
+               int ensure_write)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       BUG_ON(to & (PAGE_SIZE - 1));
+       BUG_ON(len & (PAGE_SIZE - 1));
+
+       if (super->s_flags & LOGFS_SB_FLAG_RO)
+               return -EROFS;
+
+       if (ensure_write) {
+               /*
+                * Object store doesn't care whether erases happen or not.
+                * But for the journal they are required.  Otherwise a scan
+                * can find an old commit entry and assume it is the current
+                * one, travelling back in time.
+                */
+               do_erase(sb, to, to >> PAGE_SHIFT, len >> PAGE_SHIFT);
+       }
+
+       return 0;
+}
+
+static void bdev_sync(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       wait_event(wq, atomic_read(&super->s_pending_writes) == 0);
+}
+
+static struct page *bdev_find_first_sb(struct super_block *sb, u64 *ofs)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       filler_t *filler = bdev_readpage;
+
+       *ofs = 0;
+       return read_cache_page(mapping, 0, filler, sb);
+}
+
+static struct page *bdev_find_last_sb(struct super_block *sb, u64 *ofs)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       filler_t *filler = bdev_readpage;
+       u64 pos = (super->s_bdev->bd_inode->i_size & ~0xfffULL) - 0x1000;
+       pgoff_t index = pos >> PAGE_SHIFT;
+
+       *ofs = pos;
+       return read_cache_page(mapping, index, filler, sb);
+}
+
+static int bdev_write_sb(struct super_block *sb, struct page *page)
+{
+       struct block_device *bdev = logfs_super(sb)->s_bdev;
+
+       /* Nothing special to do for block devices. */
+       return sync_request(page, bdev, WRITE);
+}
+
+static void bdev_put_device(struct super_block *sb)
+{
+       close_bdev_exclusive(logfs_super(sb)->s_bdev, FMODE_READ|FMODE_WRITE);
+}
+
+static const struct logfs_device_ops bd_devops = {
+       .find_first_sb  = bdev_find_first_sb,
+       .find_last_sb   = bdev_find_last_sb,
+       .write_sb       = bdev_write_sb,
+       .readpage       = bdev_readpage,
+       .writeseg       = bdev_writeseg,
+       .erase          = bdev_erase,
+       .sync           = bdev_sync,
+       .put_device     = bdev_put_device,
+};
+
+int logfs_get_sb_bdev(struct file_system_type *type, int flags,
+               const char *devname, struct vfsmount *mnt)
+{
+       struct block_device *bdev;
+
+       bdev = open_bdev_exclusive(devname, FMODE_READ|FMODE_WRITE, type);
+       if (IS_ERR(bdev))
+               return PTR_ERR(bdev);
+
+       if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
+               int mtdnr = MINOR(bdev->bd_dev);
+               close_bdev_exclusive(bdev, FMODE_READ|FMODE_WRITE);
+               return logfs_get_sb_mtd(type, flags, mtdnr, mnt);
+       }
+
+       return logfs_get_sb_device(type, flags, NULL, bdev, &bd_devops, mnt);
+}
diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c
new file mode 100644 (file)
index 0000000..cafb6ef
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * fs/logfs/dev_mtd.c  - Device access methods for MTD
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+#include <linux/completion.h>
+#include <linux/mount.h>
+#include <linux/sched.h>
+
+#define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1))
+
+static int mtd_read(struct super_block *sb, loff_t ofs, size_t len, void *buf)
+{
+       struct mtd_info *mtd = logfs_super(sb)->s_mtd;
+       size_t retlen;
+       int ret;
+
+       ret = mtd->read(mtd, ofs, len, &retlen, buf);
+       BUG_ON(ret == -EINVAL);
+       if (ret)
+               return ret;
+
+       /* Not sure if we should loop instead. */
+       if (retlen != len)
+               return -EIO;
+
+       return 0;
+}
+
+static int mtd_write(struct super_block *sb, loff_t ofs, size_t len, void *buf)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct mtd_info *mtd = super->s_mtd;
+       size_t retlen;
+       loff_t page_start, page_end;
+       int ret;
+
+       if (super->s_flags & LOGFS_SB_FLAG_RO)
+               return -EROFS;
+
+       BUG_ON((ofs >= mtd->size) || (len > mtd->size - ofs));
+       BUG_ON(ofs != (ofs >> super->s_writeshift) << super->s_writeshift);
+       BUG_ON(len > PAGE_CACHE_SIZE);
+       page_start = ofs & PAGE_CACHE_MASK;
+       page_end = PAGE_CACHE_ALIGN(ofs + len) - 1;
+       ret = mtd->write(mtd, ofs, len, &retlen, buf);
+       if (ret || (retlen != len))
+               return -EIO;
+
+       return 0;
+}
+
+/*
+ * For as long as I can remember (since about 2001) mtd->erase has been an
+ * asynchronous interface lacking the first driver to actually use the
+ * asynchronous properties.  So just to prevent the first implementor of such
+ * a thing from breaking logfs in 2350, we do the usual pointless dance to
+ * declare a completion variable and wait for completion before returning
+ * from mtd_erase().  What an excercise in futility!
+ */
+static void logfs_erase_callback(struct erase_info *ei)
+{
+       complete((struct completion *)ei->priv);
+}
+
+static int mtd_erase_mapping(struct super_block *sb, loff_t ofs, size_t len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       struct page *page;
+       pgoff_t index = ofs >> PAGE_SHIFT;
+
+       for (index = ofs >> PAGE_SHIFT; index < (ofs + len) >> PAGE_SHIFT; index++) {
+               page = find_get_page(mapping, index);
+               if (!page)
+                       continue;
+               memset(page_address(page), 0xFF, PAGE_SIZE);
+               page_cache_release(page);
+       }
+       return 0;
+}
+
+static int mtd_erase(struct super_block *sb, loff_t ofs, size_t len,
+               int ensure_write)
+{
+       struct mtd_info *mtd = logfs_super(sb)->s_mtd;
+       struct erase_info ei;
+       DECLARE_COMPLETION_ONSTACK(complete);
+       int ret;
+
+       BUG_ON(len % mtd->erasesize);
+       if (logfs_super(sb)->s_flags & LOGFS_SB_FLAG_RO)
+               return -EROFS;
+
+       memset(&ei, 0, sizeof(ei));
+       ei.mtd = mtd;
+       ei.addr = ofs;
+       ei.len = len;
+       ei.callback = logfs_erase_callback;
+       ei.priv = (long)&complete;
+       ret = mtd->erase(mtd, &ei);
+       if (ret)
+               return -EIO;
+
+       wait_for_completion(&complete);
+       if (ei.state != MTD_ERASE_DONE)
+               return -EIO;
+       return mtd_erase_mapping(sb, ofs, len);
+}
+
+static void mtd_sync(struct super_block *sb)
+{
+       struct mtd_info *mtd = logfs_super(sb)->s_mtd;
+
+       if (mtd->sync)
+               mtd->sync(mtd);
+}
+
+static int mtd_readpage(void *_sb, struct page *page)
+{
+       struct super_block *sb = _sb;
+       int err;
+
+       err = mtd_read(sb, page->index << PAGE_SHIFT, PAGE_SIZE,
+                       page_address(page));
+       if (err == -EUCLEAN) {
+               err = 0;
+               /* FIXME: force GC this segment */
+       }
+       if (err) {
+               ClearPageUptodate(page);
+               SetPageError(page);
+       } else {
+               SetPageUptodate(page);
+               ClearPageError(page);
+       }
+       unlock_page(page);
+       return err;
+}
+
+static struct page *mtd_find_first_sb(struct super_block *sb, u64 *ofs)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       filler_t *filler = mtd_readpage;
+       struct mtd_info *mtd = super->s_mtd;
+
+       if (!mtd->block_isbad)
+               return NULL;
+
+       *ofs = 0;
+       while (mtd->block_isbad(mtd, *ofs)) {
+               *ofs += mtd->erasesize;
+               if (*ofs >= mtd->size)
+                       return NULL;
+       }
+       BUG_ON(*ofs & ~PAGE_MASK);
+       return read_cache_page(mapping, *ofs >> PAGE_SHIFT, filler, sb);
+}
+
+static struct page *mtd_find_last_sb(struct super_block *sb, u64 *ofs)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       filler_t *filler = mtd_readpage;
+       struct mtd_info *mtd = super->s_mtd;
+
+       if (!mtd->block_isbad)
+               return NULL;
+
+       *ofs = mtd->size - mtd->erasesize;
+       while (mtd->block_isbad(mtd, *ofs)) {
+               *ofs -= mtd->erasesize;
+               if (*ofs <= 0)
+                       return NULL;
+       }
+       *ofs = *ofs + mtd->erasesize - 0x1000;
+       BUG_ON(*ofs & ~PAGE_MASK);
+       return read_cache_page(mapping, *ofs >> PAGE_SHIFT, filler, sb);
+}
+
+static int __mtd_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
+               size_t nr_pages)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       struct page *page;
+       int i, err;
+
+       for (i = 0; i < nr_pages; i++) {
+               page = find_lock_page(mapping, index + i);
+               BUG_ON(!page);
+
+               err = mtd_write(sb, page->index << PAGE_SHIFT, PAGE_SIZE,
+                               page_address(page));
+               unlock_page(page);
+               page_cache_release(page);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static void mtd_writeseg(struct super_block *sb, u64 ofs, size_t len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int head;
+
+       if (super->s_flags & LOGFS_SB_FLAG_RO)
+               return;
+
+       if (len == 0) {
+               /* This can happen when the object fit perfectly into a
+                * segment, the segment gets written per sync and subsequently
+                * closed.
+                */
+               return;
+       }
+       head = ofs & (PAGE_SIZE - 1);
+       if (head) {
+               ofs -= head;
+               len += head;
+       }
+       len = PAGE_ALIGN(len);
+       __mtd_writeseg(sb, ofs, ofs >> PAGE_SHIFT, len >> PAGE_SHIFT);
+}
+
+static void mtd_put_device(struct super_block *sb)
+{
+       put_mtd_device(logfs_super(sb)->s_mtd);
+}
+
+static const struct logfs_device_ops mtd_devops = {
+       .find_first_sb  = mtd_find_first_sb,
+       .find_last_sb   = mtd_find_last_sb,
+       .readpage       = mtd_readpage,
+       .writeseg       = mtd_writeseg,
+       .erase          = mtd_erase,
+       .sync           = mtd_sync,
+       .put_device     = mtd_put_device,
+};
+
+int logfs_get_sb_mtd(struct file_system_type *type, int flags,
+               int mtdnr, struct vfsmount *mnt)
+{
+       struct mtd_info *mtd;
+       const struct logfs_device_ops *devops = &mtd_devops;
+
+       mtd = get_mtd_device(NULL, mtdnr);
+       return logfs_get_sb_device(type, flags, mtd, NULL, devops, mnt);
+}
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
new file mode 100644 (file)
index 0000000..56a8bfb
--- /dev/null
@@ -0,0 +1,827 @@
+/*
+ * fs/logfs/dir.c      - directory-related code
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+
+
+/*
+ * Atomic dir operations
+ *
+ * Directory operations are by default not atomic.  Dentries and Inodes are
+ * created/removed/altered in seperate operations.  Therefore we need to do
+ * a small amount of journaling.
+ *
+ * Create, link, mkdir, mknod and symlink all share the same function to do
+ * the work: __logfs_create.  This function works in two atomic steps:
+ * 1. allocate inode (remember in journal)
+ * 2. allocate dentry (clear journal)
+ *
+ * As we can only get interrupted between the two, when the inode we just
+ * created is simply stored in the anchor.  On next mount, if we were
+ * interrupted, we delete the inode.  From a users point of view the
+ * operation never happened.
+ *
+ * Unlink and rmdir also share the same function: unlink.  Again, this
+ * function works in two atomic steps
+ * 1. remove dentry (remember inode in journal)
+ * 2. unlink inode (clear journal)
+ *
+ * And again, on the next mount, if we were interrupted, we delete the inode.
+ * From a users point of view the operation succeeded.
+ *
+ * Rename is the real pain to deal with, harder than all the other methods
+ * combined.  Depending on the circumstances we can run into three cases.
+ * A "target rename" where the target dentry already existed, a "local
+ * rename" where both parent directories are identical or a "cross-directory
+ * rename" in the remaining case.
+ *
+ * Local rename is atomic, as the old dentry is simply rewritten with a new
+ * name.
+ *
+ * Cross-directory rename works in two steps, similar to __logfs_create and
+ * logfs_unlink:
+ * 1. Write new dentry (remember old dentry in journal)
+ * 2. Remove old dentry (clear journal)
+ *
+ * Here we remember a dentry instead of an inode.  On next mount, if we were
+ * interrupted, we delete the dentry.  From a users point of view, the
+ * operation succeeded.
+ *
+ * Target rename works in three atomic steps:
+ * 1. Attach old inode to new dentry (remember old dentry and new inode)
+ * 2. Remove old dentry (still remember the new inode)
+ * 3. Remove victim inode
+ *
+ * Here we remember both an inode an a dentry.  If we get interrupted
+ * between steps 1 and 2, we delete both the dentry and the inode.  If
+ * we get interrupted between steps 2 and 3, we delete just the inode.
+ * In either case, the remaining objects are deleted on next mount.  From
+ * a users point of view, the operation succeeded.
+ */
+
+static int write_dir(struct inode *dir, struct logfs_disk_dentry *dd,
+               loff_t pos)
+{
+       return logfs_inode_write(dir, dd, sizeof(*dd), pos, WF_LOCK, NULL);
+}
+
+static int write_inode(struct inode *inode)
+{
+       return __logfs_write_inode(inode, WF_LOCK);
+}
+
+static s64 dir_seek_data(struct inode *inode, s64 pos)
+{
+       s64 new_pos = logfs_seek_data(inode, pos);
+
+       return max(pos, new_pos - 1);
+}
+
+static int beyond_eof(struct inode *inode, loff_t bix)
+{
+       loff_t pos = bix << inode->i_sb->s_blocksize_bits;
+       return pos >= i_size_read(inode);
+}
+
+/*
+ * Prime value was chosen to be roughly 256 + 26.  r5 hash uses 11,
+ * so short names (len <= 9) don't even occupy the complete 32bit name
+ * space.  A prime >256 ensures short names quickly spread the 32bit
+ * name space.  Add about 26 for the estimated amount of information
+ * of each character and pick a prime nearby, preferrably a bit-sparse
+ * one.
+ */
+static u32 hash_32(const char *s, int len, u32 seed)
+{
+       u32 hash = seed;
+       int i;
+
+       for (i = 0; i < len; i++)
+               hash = hash * 293 + s[i];
+       return hash;
+}
+
+/*
+ * We have to satisfy several conflicting requirements here.  Small
+ * directories should stay fairly compact and not require too many
+ * indirect blocks.  The number of possible locations for a given hash
+ * should be small to make lookup() fast.  And we should try hard not
+ * to overflow the 32bit name space or nfs and 32bit host systems will
+ * be unhappy.
+ *
+ * So we use the following scheme.  First we reduce the hash to 0..15
+ * and try a direct block.  If that is occupied we reduce the hash to
+ * 16..255 and try an indirect block.  Same for 2x and 3x indirect
+ * blocks.  Lastly we reduce the hash to 0x800_0000 .. 0xffff_ffff,
+ * but use buckets containing eight entries instead of a single one.
+ *
+ * Using 16 entries should allow for a reasonable amount of hash
+ * collisions, so the 32bit name space can be packed fairly tight
+ * before overflowing.  Oh and currently we don't overflow but return
+ * and error.
+ *
+ * How likely are collisions?  Doing the appropriate math is beyond me
+ * and the Bronstein textbook.  But running a test program to brute
+ * force collisions for a couple of days showed that on average the
+ * first collision occurs after 598M entries, with 290M being the
+ * smallest result.  Obviously 21 entries could already cause a
+ * collision if all entries are carefully chosen.
+ */
+static pgoff_t hash_index(u32 hash, int round)
+{
+       u32 i0_blocks = I0_BLOCKS;
+       u32 i1_blocks = I1_BLOCKS;
+       u32 i2_blocks = I2_BLOCKS;
+       u32 i3_blocks = I3_BLOCKS;
+
+       switch (round) {
+       case 0:
+               return hash % i0_blocks;
+       case 1:
+               return i0_blocks + hash % (i1_blocks - i0_blocks);
+       case 2:
+               return i1_blocks + hash % (i2_blocks - i1_blocks);
+       case 3:
+               return i2_blocks + hash % (i3_blocks - i2_blocks);
+       case 4 ... 19:
+               return i3_blocks + 16 * (hash % (((1<<31) - i3_blocks) / 16))
+                       + round - 4;
+       }
+       BUG();
+}
+
+static struct page *logfs_get_dd_page(struct inode *dir, struct dentry *dentry)
+{
+       struct qstr *name = &dentry->d_name;
+       struct page *page;
+       struct logfs_disk_dentry *dd;
+       u32 hash = hash_32(name->name, name->len, 0);
+       pgoff_t index;
+       int round;
+
+       if (name->len > LOGFS_MAX_NAMELEN)
+               return ERR_PTR(-ENAMETOOLONG);
+
+       for (round = 0; round < 20; round++) {
+               index = hash_index(hash, round);
+
+               if (beyond_eof(dir, index))
+                       return NULL;
+               if (!logfs_exist_block(dir, index))
+                       continue;
+               page = read_cache_page(dir->i_mapping, index,
+                               (filler_t *)logfs_readpage, NULL);
+               if (IS_ERR(page))
+                       return page;
+               dd = kmap_atomic(page, KM_USER0);
+               BUG_ON(dd->namelen == 0);
+
+               if (name->len != be16_to_cpu(dd->namelen) ||
+                               memcmp(name->name, dd->name, name->len)) {
+                       kunmap_atomic(dd, KM_USER0);
+                       page_cache_release(page);
+                       continue;
+               }
+
+               kunmap_atomic(dd, KM_USER0);
+               return page;
+       }
+       return NULL;
+}
+
+static int logfs_remove_inode(struct inode *inode)
+{
+       int ret;
+
+       inode->i_nlink--;
+       ret = write_inode(inode);
+       LOGFS_BUG_ON(ret, inode->i_sb);
+       return ret;
+}
+
+static void abort_transaction(struct inode *inode, struct logfs_transaction *ta)
+{
+       if (logfs_inode(inode)->li_block)
+               logfs_inode(inode)->li_block->ta = NULL;
+       kfree(ta);
+}
+
+static int logfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+       struct logfs_super *super = logfs_super(dir->i_sb);
+       struct inode *inode = dentry->d_inode;
+       struct logfs_transaction *ta;
+       struct page *page;
+       pgoff_t index;
+       int ret;
+
+       ta = kzalloc(sizeof(*ta), GFP_KERNEL);
+       if (!ta)
+               return -ENOMEM;
+
+       ta->state = UNLINK_1;
+       ta->ino = inode->i_ino;
+
+       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+
+       page = logfs_get_dd_page(dir, dentry);
+       if (!page) {
+               kfree(ta);
+               return -ENOENT;
+       }
+       if (IS_ERR(page)) {
+               kfree(ta);
+               return PTR_ERR(page);
+       }
+       index = page->index;
+       page_cache_release(page);
+
+       mutex_lock(&super->s_dirop_mutex);
+       logfs_add_transaction(dir, ta);
+
+       ret = logfs_delete(dir, index, NULL);
+       if (!ret)
+               ret = write_inode(dir);
+
+       if (ret) {
+               abort_transaction(dir, ta);
+               printk(KERN_ERR"LOGFS: unable to delete inode\n");
+               goto out;
+       }
+
+       ta->state = UNLINK_2;
+       logfs_add_transaction(inode, ta);
+       ret = logfs_remove_inode(inode);
+out:
+       mutex_unlock(&super->s_dirop_mutex);
+       return ret;
+}
+
+static inline int logfs_empty_dir(struct inode *dir)
+{
+       u64 data;
+
+       data = logfs_seek_data(dir, 0) << dir->i_sb->s_blocksize_bits;
+       return data >= i_size_read(dir);
+}
+
+static int logfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!logfs_empty_dir(inode))
+               return -ENOTEMPTY;
+
+       return logfs_unlink(dir, dentry);
+}
+
+/* FIXME: readdir currently has it's own dir_walk code.  I don't see a good
+ * way to combine the two copies */
+#define IMPLICIT_NODES 2
+static int __logfs_readdir(struct file *file, void *buf, filldir_t filldir)
+{
+       struct inode *dir = file->f_dentry->d_inode;
+       loff_t pos = file->f_pos - IMPLICIT_NODES;
+       struct page *page;
+       struct logfs_disk_dentry *dd;
+       int full;
+
+       BUG_ON(pos < 0);
+       for (;; pos++) {
+               if (beyond_eof(dir, pos))
+                       break;
+               if (!logfs_exist_block(dir, pos)) {
+                       /* deleted dentry */
+                       pos = dir_seek_data(dir, pos);
+                       continue;
+               }
+               page = read_cache_page(dir->i_mapping, pos,
+                               (filler_t *)logfs_readpage, NULL);
+               if (IS_ERR(page))
+                       return PTR_ERR(page);
+               dd = kmap_atomic(page, KM_USER0);
+               BUG_ON(dd->namelen == 0);
+
+               full = filldir(buf, (char *)dd->name, be16_to_cpu(dd->namelen),
+                               pos, be64_to_cpu(dd->ino), dd->type);
+               kunmap_atomic(dd, KM_USER0);
+               page_cache_release(page);
+               if (full)
+                       break;
+       }
+
+       file->f_pos = pos + IMPLICIT_NODES;
+       return 0;
+}
+
+static int logfs_readdir(struct file *file, void *buf, filldir_t filldir)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       ino_t pino = parent_ino(file->f_dentry);
+       int err;
+
+       if (file->f_pos < 0)
+               return -EINVAL;
+
+       if (file->f_pos == 0) {
+               if (filldir(buf, ".", 1, 1, inode->i_ino, DT_DIR) < 0)
+                       return 0;
+               file->f_pos++;
+       }
+       if (file->f_pos == 1) {
+               if (filldir(buf, "..", 2, 2, pino, DT_DIR) < 0)
+                       return 0;
+               file->f_pos++;
+       }
+
+       err = __logfs_readdir(file, buf, filldir);
+       return err;
+}
+
+static void logfs_set_name(struct logfs_disk_dentry *dd, struct qstr *name)
+{
+       dd->namelen = cpu_to_be16(name->len);
+       memcpy(dd->name, name->name, name->len);
+}
+
+static struct dentry *logfs_lookup(struct inode *dir, struct dentry *dentry,
+               struct nameidata *nd)
+{
+       struct page *page;
+       struct logfs_disk_dentry *dd;
+       pgoff_t index;
+       u64 ino = 0;
+       struct inode *inode;
+
+       page = logfs_get_dd_page(dir, dentry);
+       if (IS_ERR(page))
+               return ERR_CAST(page);
+       if (!page) {
+               d_add(dentry, NULL);
+               return NULL;
+       }
+       index = page->index;
+       dd = kmap_atomic(page, KM_USER0);
+       ino = be64_to_cpu(dd->ino);
+       kunmap_atomic(dd, KM_USER0);
+       page_cache_release(page);
+
+       inode = logfs_iget(dir->i_sb, ino);
+       if (IS_ERR(inode)) {
+               printk(KERN_ERR"LogFS: Cannot read inode #%llx for dentry (%lx, %lx)n",
+                               ino, dir->i_ino, index);
+               return ERR_CAST(inode);
+       }
+       return d_splice_alias(inode, dentry);
+}
+
+static void grow_dir(struct inode *dir, loff_t index)
+{
+       index = (index + 1) << dir->i_sb->s_blocksize_bits;
+       if (i_size_read(dir) < index)
+               i_size_write(dir, index);
+}
+
+static int logfs_write_dir(struct inode *dir, struct dentry *dentry,
+               struct inode *inode)
+{
+       struct page *page;
+       struct logfs_disk_dentry *dd;
+       u32 hash = hash_32(dentry->d_name.name, dentry->d_name.len, 0);
+       pgoff_t index;
+       int round, err;
+
+       for (round = 0; round < 20; round++) {
+               index = hash_index(hash, round);
+
+               if (logfs_exist_block(dir, index))
+                       continue;
+               page = find_or_create_page(dir->i_mapping, index, GFP_KERNEL);
+               if (!page)
+                       return -ENOMEM;
+
+               dd = kmap_atomic(page, KM_USER0);
+               memset(dd, 0, sizeof(*dd));
+               dd->ino = cpu_to_be64(inode->i_ino);
+               dd->type = logfs_type(inode);
+               logfs_set_name(dd, &dentry->d_name);
+               kunmap_atomic(dd, KM_USER0);
+
+               err = logfs_write_buf(dir, page, WF_LOCK);
+               unlock_page(page);
+               page_cache_release(page);
+               if (!err)
+                       grow_dir(dir, index);
+               return err;
+       }
+       /* FIXME: Is there a better return value?  In most cases neither
+        * the filesystem nor the directory are full.  But we have had
+        * too many collisions for this particular hash and no fallback.
+        */
+       return -ENOSPC;
+}
+
+static int __logfs_create(struct inode *dir, struct dentry *dentry,
+               struct inode *inode, const char *dest, long destlen)
+{
+       struct logfs_super *super = logfs_super(dir->i_sb);
+       struct logfs_inode *li = logfs_inode(inode);
+       struct logfs_transaction *ta;
+       int ret;
+
+       ta = kzalloc(sizeof(*ta), GFP_KERNEL);
+       if (!ta)
+               return -ENOMEM;
+
+       ta->state = CREATE_1;
+       ta->ino = inode->i_ino;
+       mutex_lock(&super->s_dirop_mutex);
+       logfs_add_transaction(inode, ta);
+
+       if (dest) {
+               /* symlink */
+               ret = logfs_inode_write(inode, dest, destlen, 0, WF_LOCK, NULL);
+               if (!ret)
+                       ret = write_inode(inode);
+       } else {
+               /* creat/mkdir/mknod */
+               ret = write_inode(inode);
+       }
+       if (ret) {
+               abort_transaction(inode, ta);
+               li->li_flags |= LOGFS_IF_STILLBORN;
+               /* FIXME: truncate symlink */
+               inode->i_nlink--;
+               iput(inode);
+               goto out;
+       }
+
+       ta->state = CREATE_2;
+       logfs_add_transaction(dir, ta);
+       ret = logfs_write_dir(dir, dentry, inode);
+       /* sync directory */
+       if (!ret)
+               ret = write_inode(dir);
+
+       if (ret) {
+               logfs_del_transaction(dir, ta);
+               ta->state = CREATE_2;
+               logfs_add_transaction(inode, ta);
+               logfs_remove_inode(inode);
+               iput(inode);
+               goto out;
+       }
+       d_instantiate(dentry, inode);
+out:
+       mutex_unlock(&super->s_dirop_mutex);
+       return ret;
+}
+
+static int logfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       struct inode *inode;
+
+       /*
+        * FIXME: why do we have to fill in S_IFDIR, while the mode is
+        * correct for mknod, creat, etc.?  Smells like the vfs *should*
+        * do it for us but for some reason fails to do so.
+        */
+       inode = logfs_new_inode(dir, S_IFDIR | mode);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = &logfs_dir_iops;
+       inode->i_fop = &logfs_dir_fops;
+
+       return __logfs_create(dir, dentry, inode, NULL, 0);
+}
+
+static int logfs_create(struct inode *dir, struct dentry *dentry, int mode,
+               struct nameidata *nd)
+{
+       struct inode *inode;
+
+       inode = logfs_new_inode(dir, mode);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = &logfs_reg_iops;
+       inode->i_fop = &logfs_reg_fops;
+       inode->i_mapping->a_ops = &logfs_reg_aops;
+
+       return __logfs_create(dir, dentry, inode, NULL, 0);
+}
+
+static int logfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
+               dev_t rdev)
+{
+       struct inode *inode;
+
+       if (dentry->d_name.len > LOGFS_MAX_NAMELEN)
+               return -ENAMETOOLONG;
+
+       inode = logfs_new_inode(dir, mode);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       init_special_inode(inode, mode, rdev);
+
+       return __logfs_create(dir, dentry, inode, NULL, 0);
+}
+
+static int logfs_symlink(struct inode *dir, struct dentry *dentry,
+               const char *target)
+{
+       struct inode *inode;
+       size_t destlen = strlen(target) + 1;
+
+       if (destlen > dir->i_sb->s_blocksize)
+               return -ENAMETOOLONG;
+
+       inode = logfs_new_inode(dir, S_IFLNK | 0777);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+
+       inode->i_op = &logfs_symlink_iops;
+       inode->i_mapping->a_ops = &logfs_reg_aops;
+
+       return __logfs_create(dir, dentry, inode, target, destlen);
+}
+
+static int logfs_permission(struct inode *inode, int mask)
+{
+       return generic_permission(inode, mask, NULL);
+}
+
+static int logfs_link(struct dentry *old_dentry, struct inode *dir,
+               struct dentry *dentry)
+{
+       struct inode *inode = old_dentry->d_inode;
+
+       if (inode->i_nlink >= LOGFS_LINK_MAX)
+               return -EMLINK;
+
+       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+       atomic_inc(&inode->i_count);
+       inode->i_nlink++;
+       mark_inode_dirty_sync(inode);
+
+       return __logfs_create(dir, dentry, inode, NULL, 0);
+}
+
+static int logfs_get_dd(struct inode *dir, struct dentry *dentry,
+               struct logfs_disk_dentry *dd, loff_t *pos)
+{
+       struct page *page;
+       void *map;
+
+       page = logfs_get_dd_page(dir, dentry);
+       if (IS_ERR(page))
+               return PTR_ERR(page);
+       *pos = page->index;
+       map = kmap_atomic(page, KM_USER0);
+       memcpy(dd, map, sizeof(*dd));
+       kunmap_atomic(map, KM_USER0);
+       page_cache_release(page);
+       return 0;
+}
+
+static int logfs_delete_dd(struct inode *dir, loff_t pos)
+{
+       /*
+        * Getting called with pos somewhere beyond eof is either a goofup
+        * within this file or means someone maliciously edited the
+        * (crc-protected) journal.
+        */
+       BUG_ON(beyond_eof(dir, pos));
+       dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+       log_dir(" Delete dentry (%lx, %llx)\n", dir->i_ino, pos);
+       return logfs_delete(dir, pos, NULL);
+}
+
+/*
+ * Cross-directory rename, target does not exist.  Just a little nasty.
+ * Create a new dentry in the target dir, then remove the old dentry,
+ * all the while taking care to remember our operation in the journal.
+ */
+static int logfs_rename_cross(struct inode *old_dir, struct dentry *old_dentry,
+                             struct inode *new_dir, struct dentry *new_dentry)
+{
+       struct logfs_super *super = logfs_super(old_dir->i_sb);
+       struct logfs_disk_dentry dd;
+       struct logfs_transaction *ta;
+       loff_t pos;
+       int err;
+
+       /* 1. locate source dd */
+       err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
+       if (err)
+               return err;
+
+       ta = kzalloc(sizeof(*ta), GFP_KERNEL);
+       if (!ta)
+               return -ENOMEM;
+
+       ta->state = CROSS_RENAME_1;
+       ta->dir = old_dir->i_ino;
+       ta->pos = pos;
+
+       /* 2. write target dd */
+       mutex_lock(&super->s_dirop_mutex);
+       logfs_add_transaction(new_dir, ta);
+       err = logfs_write_dir(new_dir, new_dentry, old_dentry->d_inode);
+       if (!err)
+               err = write_inode(new_dir);
+
+       if (err) {
+               super->s_rename_dir = 0;
+               super->s_rename_pos = 0;
+               abort_transaction(new_dir, ta);
+               goto out;
+       }
+
+       /* 3. remove source dd */
+       ta->state = CROSS_RENAME_2;
+       logfs_add_transaction(old_dir, ta);
+       err = logfs_delete_dd(old_dir, pos);
+       if (!err)
+               err = write_inode(old_dir);
+       LOGFS_BUG_ON(err, old_dir->i_sb);
+out:
+       mutex_unlock(&super->s_dirop_mutex);
+       return err;
+}
+
+static int logfs_replace_inode(struct inode *dir, struct dentry *dentry,
+               struct logfs_disk_dentry *dd, struct inode *inode)
+{
+       loff_t pos;
+       int err;
+
+       err = logfs_get_dd(dir, dentry, dd, &pos);
+       if (err)
+               return err;
+       dd->ino = cpu_to_be64(inode->i_ino);
+       dd->type = logfs_type(inode);
+
+       err = write_dir(dir, dd, pos);
+       if (err)
+               return err;
+       log_dir("Replace dentry (%lx, %llx) %s -> %llx\n", dir->i_ino, pos,
+                       dd->name, be64_to_cpu(dd->ino));
+       return write_inode(dir);
+}
+
+/* Target dentry exists - the worst case.  We need to attach the source
+ * inode to the target dentry, then remove the orphaned target inode and
+ * source dentry.
+ */
+static int logfs_rename_target(struct inode *old_dir, struct dentry *old_dentry,
+                              struct inode *new_dir, struct dentry *new_dentry)
+{
+       struct logfs_super *super = logfs_super(old_dir->i_sb);
+       struct inode *old_inode = old_dentry->d_inode;
+       struct inode *new_inode = new_dentry->d_inode;
+       int isdir = S_ISDIR(old_inode->i_mode);
+       struct logfs_disk_dentry dd;
+       struct logfs_transaction *ta;
+       loff_t pos;
+       int err;
+
+       BUG_ON(isdir != S_ISDIR(new_inode->i_mode));
+       if (isdir) {
+               if (!logfs_empty_dir(new_inode))
+                       return -ENOTEMPTY;
+       }
+
+       /* 1. locate source dd */
+       err = logfs_get_dd(old_dir, old_dentry, &dd, &pos);
+       if (err)
+               return err;
+
+       ta = kzalloc(sizeof(*ta), GFP_KERNEL);
+       if (!ta)
+               return -ENOMEM;
+
+       ta->state = TARGET_RENAME_1;
+       ta->dir = old_dir->i_ino;
+       ta->pos = pos;
+       ta->ino = new_inode->i_ino;
+
+       /* 2. attach source inode to target dd */
+       mutex_lock(&super->s_dirop_mutex);
+       logfs_add_transaction(new_dir, ta);
+       err = logfs_replace_inode(new_dir, new_dentry, &dd, old_inode);
+       if (err) {
+               super->s_rename_dir = 0;
+               super->s_rename_pos = 0;
+               super->s_victim_ino = 0;
+               abort_transaction(new_dir, ta);
+               goto out;
+       }
+
+       /* 3. remove source dd */
+       ta->state = TARGET_RENAME_2;
+       logfs_add_transaction(old_dir, ta);
+       err = logfs_delete_dd(old_dir, pos);
+       if (!err)
+               err = write_inode(old_dir);
+       LOGFS_BUG_ON(err, old_dir->i_sb);
+
+       /* 4. remove target inode */
+       ta->state = TARGET_RENAME_3;
+       logfs_add_transaction(new_inode, ta);
+       err = logfs_remove_inode(new_inode);
+
+out:
+       mutex_unlock(&super->s_dirop_mutex);
+       return err;
+}
+
+static int logfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+                       struct inode *new_dir, struct dentry *new_dentry)
+{
+       if (new_dentry->d_inode)
+               return logfs_rename_target(old_dir, old_dentry,
+                                          new_dir, new_dentry);
+       return logfs_rename_cross(old_dir, old_dentry, new_dir, new_dentry);
+}
+
+/* No locking done here, as this is called before .get_sb() returns. */
+int logfs_replay_journal(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *inode;
+       u64 ino, pos;
+       int err;
+
+       if (super->s_victim_ino) {
+               /* delete victim inode */
+               ino = super->s_victim_ino;
+               printk(KERN_INFO"LogFS: delete unmapped inode #%llx\n", ino);
+               inode = logfs_iget(sb, ino);
+               if (IS_ERR(inode))
+                       goto fail;
+
+               LOGFS_BUG_ON(i_size_read(inode) > 0, sb);
+               super->s_victim_ino = 0;
+               err = logfs_remove_inode(inode);
+               iput(inode);
+               if (err) {
+                       super->s_victim_ino = ino;
+                       goto fail;
+               }
+       }
+       if (super->s_rename_dir) {
+               /* delete old dd from rename */
+               ino = super->s_rename_dir;
+               pos = super->s_rename_pos;
+               printk(KERN_INFO"LogFS: delete unbacked dentry (%llx, %llx)\n",
+                               ino, pos);
+               inode = logfs_iget(sb, ino);
+               if (IS_ERR(inode))
+                       goto fail;
+
+               super->s_rename_dir = 0;
+               super->s_rename_pos = 0;
+               err = logfs_delete_dd(inode, pos);
+               iput(inode);
+               if (err) {
+                       super->s_rename_dir = ino;
+                       super->s_rename_pos = pos;
+                       goto fail;
+               }
+       }
+       return 0;
+fail:
+       LOGFS_BUG(sb);
+       return -EIO;
+}
+
+const struct inode_operations logfs_symlink_iops = {
+       .readlink       = generic_readlink,
+       .follow_link    = page_follow_link_light,
+};
+
+const struct inode_operations logfs_dir_iops = {
+       .create         = logfs_create,
+       .link           = logfs_link,
+       .lookup         = logfs_lookup,
+       .mkdir          = logfs_mkdir,
+       .mknod          = logfs_mknod,
+       .rename         = logfs_rename,
+       .rmdir          = logfs_rmdir,
+       .permission     = logfs_permission,
+       .symlink        = logfs_symlink,
+       .unlink         = logfs_unlink,
+};
+const struct file_operations logfs_dir_fops = {
+       .fsync          = logfs_fsync,
+       .ioctl          = logfs_ioctl,
+       .readdir        = logfs_readdir,
+       .read           = generic_read_dir,
+};
diff --git a/fs/logfs/file.c b/fs/logfs/file.c
new file mode 100644 (file)
index 0000000..370f367
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * fs/logfs/file.c     - prepare_write, commit_write and friends
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+#include <linux/sched.h>
+#include <linux/writeback.h>
+
+static int logfs_write_begin(struct file *file, struct address_space *mapping,
+               loff_t pos, unsigned len, unsigned flags,
+               struct page **pagep, void **fsdata)
+{
+       struct inode *inode = mapping->host;
+       struct page *page;
+       pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+
+       page = grab_cache_page_write_begin(mapping, index, flags);
+       if (!page)
+               return -ENOMEM;
+       *pagep = page;
+
+       if ((len == PAGE_CACHE_SIZE) || PageUptodate(page))
+               return 0;
+       if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
+               unsigned start = pos & (PAGE_CACHE_SIZE - 1);
+               unsigned end = start + len;
+
+               /* Reading beyond i_size is simple: memset to zero */
+               zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE);
+               return 0;
+       }
+       return logfs_readpage_nolock(page);
+}
+
+static int logfs_write_end(struct file *file, struct address_space *mapping,
+               loff_t pos, unsigned len, unsigned copied, struct page *page,
+               void *fsdata)
+{
+       struct inode *inode = mapping->host;
+       pgoff_t index = page->index;
+       unsigned start = pos & (PAGE_CACHE_SIZE - 1);
+       unsigned end = start + copied;
+       int ret = 0;
+
+       BUG_ON(PAGE_CACHE_SIZE != inode->i_sb->s_blocksize);
+       BUG_ON(page->index > I3_BLOCKS);
+
+       if (copied < len) {
+               /*
+                * Short write of a non-initialized paged.  Just tell userspace
+                * to retry the entire page.
+                */
+               if (!PageUptodate(page)) {
+                       copied = 0;
+                       goto out;
+               }
+       }
+       if (copied == 0)
+               goto out; /* FIXME: do we need to update inode? */
+
+       if (i_size_read(inode) < (index << PAGE_CACHE_SHIFT) + end) {
+               i_size_write(inode, (index << PAGE_CACHE_SHIFT) + end);
+               mark_inode_dirty_sync(inode);
+       }
+
+       SetPageUptodate(page);
+       if (!PageDirty(page)) {
+               if (!get_page_reserve(inode, page))
+                       __set_page_dirty_nobuffers(page);
+               else
+                       ret = logfs_write_buf(inode, page, WF_LOCK);
+       }
+out:
+       unlock_page(page);
+       page_cache_release(page);
+       return ret ? ret : copied;
+}
+
+int logfs_readpage(struct file *file, struct page *page)
+{
+       int ret;
+
+       ret = logfs_readpage_nolock(page);
+       unlock_page(page);
+       return ret;
+}
+
+/* Clear the page's dirty flag in the radix tree. */
+/* TODO: mucking with PageWriteback is silly.  Add a generic function to clear
+ * the dirty bit from the radix tree for filesystems that don't have to wait
+ * for page writeback to finish (i.e. any compressing filesystem).
+ */
+static void clear_radix_tree_dirty(struct page *page)
+{
+       BUG_ON(PagePrivate(page) || page->private);
+       set_page_writeback(page);
+       end_page_writeback(page);
+}
+
+static int __logfs_writepage(struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       int err;
+
+       err = logfs_write_buf(inode, page, WF_LOCK);
+       if (err)
+               set_page_dirty(page);
+       else
+               clear_radix_tree_dirty(page);
+       unlock_page(page);
+       return err;
+}
+
+static int logfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+       struct inode *inode = page->mapping->host;
+       loff_t i_size = i_size_read(inode);
+       pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT;
+       unsigned offset;
+       u64 bix;
+       level_t level;
+
+       log_file("logfs_writepage(%lx, %lx, %p)\n", inode->i_ino, page->index,
+                       page);
+
+       logfs_unpack_index(page->index, &bix, &level);
+
+       /* Indirect blocks are never truncated */
+       if (level != 0)
+               return __logfs_writepage(page);
+
+       /*
+        * TODO: everything below is a near-verbatim copy of nobh_writepage().
+        * The relevant bits should be factored out after logfs is merged.
+        */
+
+       /* Is the page fully inside i_size? */
+       if (bix < end_index)
+               return __logfs_writepage(page);
+
+        /* Is the page fully outside i_size? (truncate in progress) */
+       offset = i_size & (PAGE_CACHE_SIZE-1);
+       if (bix > end_index || offset == 0) {
+               unlock_page(page);
+               return 0; /* don't care */
+       }
+
+       /*
+        * The page straddles i_size.  It must be zeroed out on each and every
+        * writepage invokation because it may be mmapped.  "A file is mapped
+        * in multiples of the page size.  For a file that is not a multiple of
+        * the  page size, the remaining memory is zeroed when mapped, and
+        * writes to that region are not written out to the file."
+        */
+       zero_user_segment(page, offset, PAGE_CACHE_SIZE);
+       return __logfs_writepage(page);
+}
+
+static void logfs_invalidatepage(struct page *page, unsigned long offset)
+{
+       move_page_to_btree(page);
+       BUG_ON(PagePrivate(page) || page->private);
+}
+
+static int logfs_releasepage(struct page *page, gfp_t only_xfs_uses_this)
+{
+       return 0; /* None of these are easy to release */
+}
+
+
+int logfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+               unsigned long arg)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       unsigned int oldflags, flags;
+       int err;
+
+       switch (cmd) {
+       case FS_IOC_GETFLAGS:
+               flags = li->li_flags & LOGFS_FL_USER_VISIBLE;
+               return put_user(flags, (int __user *)arg);
+       case FS_IOC_SETFLAGS:
+               if (IS_RDONLY(inode))
+                       return -EROFS;
+
+               if (!is_owner_or_cap(inode))
+                       return -EACCES;
+
+               err = get_user(flags, (int __user *)arg);
+               if (err)
+                       return err;
+
+               mutex_lock(&inode->i_mutex);
+               oldflags = li->li_flags;
+               flags &= LOGFS_FL_USER_MODIFIABLE;
+               flags |= oldflags & ~LOGFS_FL_USER_MODIFIABLE;
+               li->li_flags = flags;
+               mutex_unlock(&inode->i_mutex);
+
+               inode->i_ctime = CURRENT_TIME;
+               mark_inode_dirty_sync(inode);
+               return 0;
+
+       default:
+               return -ENOTTY;
+       }
+}
+
+int logfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       struct super_block *sb = dentry->d_inode->i_sb;
+       struct logfs_super *super = logfs_super(sb);
+
+       /* FIXME: write anchor */
+       super->s_devops->sync(sb);
+       return 0;
+}
+
+static int logfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+       struct inode *inode = dentry->d_inode;
+       int err = 0;
+
+       if (attr->ia_valid & ATTR_SIZE)
+               err = logfs_truncate(inode, attr->ia_size);
+       attr->ia_valid &= ~ATTR_SIZE;
+
+       if (!err)
+               err = inode_change_ok(inode, attr);
+       if (!err)
+               err = inode_setattr(inode, attr);
+       return err;
+}
+
+const struct inode_operations logfs_reg_iops = {
+       .setattr        = logfs_setattr,
+};
+
+const struct file_operations logfs_reg_fops = {
+       .aio_read       = generic_file_aio_read,
+       .aio_write      = generic_file_aio_write,
+       .fsync          = logfs_fsync,
+       .ioctl          = logfs_ioctl,
+       .llseek         = generic_file_llseek,
+       .mmap           = generic_file_readonly_mmap,
+       .open           = generic_file_open,
+       .read           = do_sync_read,
+       .write          = do_sync_write,
+};
+
+const struct address_space_operations logfs_reg_aops = {
+       .invalidatepage = logfs_invalidatepage,
+       .readpage       = logfs_readpage,
+       .releasepage    = logfs_releasepage,
+       .set_page_dirty = __set_page_dirty_nobuffers,
+       .writepage      = logfs_writepage,
+       .writepages     = generic_writepages,
+       .write_begin    = logfs_write_begin,
+       .write_end      = logfs_write_end,
+};
diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c
new file mode 100644 (file)
index 0000000..92949f9
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * fs/logfs/gc.c       - garbage collection code
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+#include <linux/sched.h>
+
+/*
+ * Wear leveling needs to kick in when the difference between low erase
+ * counts and high erase counts gets too big.  A good value for "too big"
+ * may be somewhat below 10% of maximum erase count for the device.
+ * Why not 397, to pick a nice round number with no specific meaning? :)
+ *
+ * WL_RATELIMIT is the minimum time between two wear level events.  A huge
+ * number of segments may fulfil the requirements for wear leveling at the
+ * same time.  If that happens we don't want to cause a latency from hell,
+ * but just gently pick one segment every so often and minimize overhead.
+ */
+#define WL_DELTA 397
+#define WL_RATELIMIT 100
+#define MAX_OBJ_ALIASES        2600
+#define SCAN_RATIO 512 /* number of scanned segments per gc'd segment */
+#define LIST_SIZE 64   /* base size of candidate lists */
+#define SCAN_ROUNDS 128        /* maximum number of complete medium scans */
+#define SCAN_ROUNDS_HIGH 4 /* maximum number of higher-level scans */
+
+static int no_free_segments(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       return super->s_free_list.count;
+}
+
+/* journal has distance -1, top-most ifile layer distance 0 */
+static u8 root_distance(struct super_block *sb, gc_level_t __gc_level)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u8 gc_level = (__force u8)__gc_level;
+
+       switch (gc_level) {
+       case 0: /* fall through */
+       case 1: /* fall through */
+       case 2: /* fall through */
+       case 3:
+               /* file data or indirect blocks */
+               return super->s_ifile_levels + super->s_iblock_levels - gc_level;
+       case 6: /* fall through */
+       case 7: /* fall through */
+       case 8: /* fall through */
+       case 9:
+               /* inode file data or indirect blocks */
+               return super->s_ifile_levels - (gc_level - 6);
+       default:
+               printk(KERN_ERR"LOGFS: segment of unknown level %x found\n",
+                               gc_level);
+               WARN_ON(1);
+               return super->s_ifile_levels + super->s_iblock_levels;
+       }
+}
+
+static int segment_is_reserved(struct super_block *sb, u32 segno)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area;
+       void *reserved;
+       int i;
+
+       /* Some segments are reserved.  Just pretend they were all valid */
+       reserved = btree_lookup32(&super->s_reserved_segments, segno);
+       if (reserved)
+               return 1;
+
+       /* Currently open segments */
+       for_each_area(i) {
+               area = super->s_area[i];
+               if (area->a_is_open && area->a_segno == segno)
+                       return 1;
+       }
+
+       return 0;
+}
+
+static void logfs_mark_segment_bad(struct super_block *sb, u32 segno)
+{
+       BUG();
+}
+
+/*
+ * Returns the bytes consumed by valid objects in this segment.  Object headers
+ * are counted, the segment header is not.
+ */
+static u32 logfs_valid_bytes(struct super_block *sb, u32 segno, u32 *ec,
+               gc_level_t *gc_level)
+{
+       struct logfs_segment_entry se;
+       u32 ec_level;
+
+       logfs_get_segment_entry(sb, segno, &se);
+       if (se.ec_level == cpu_to_be32(BADSEG) ||
+                       se.valid == cpu_to_be32(RESERVED))
+               return RESERVED;
+
+       ec_level = be32_to_cpu(se.ec_level);
+       *ec = ec_level >> 4;
+       *gc_level = GC_LEVEL(ec_level & 0xf);
+       return be32_to_cpu(se.valid);
+}
+
+static void logfs_cleanse_block(struct super_block *sb, u64 ofs, u64 ino,
+               u64 bix, gc_level_t gc_level)
+{
+       struct inode *inode;
+       int err, cookie;
+
+       inode = logfs_safe_iget(sb, ino, &cookie);
+       err = logfs_rewrite_block(inode, bix, ofs, gc_level, 0);
+       BUG_ON(err);
+       logfs_safe_iput(inode, cookie);
+}
+
+static u32 logfs_gc_segment(struct super_block *sb, u32 segno, u8 dist)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_segment_header sh;
+       struct logfs_object_header oh;
+       u64 ofs, ino, bix;
+       u32 seg_ofs, logical_segno, cleaned = 0;
+       int err, len, valid;
+       gc_level_t gc_level;
+
+       LOGFS_BUG_ON(segment_is_reserved(sb, segno), sb);
+
+       btree_insert32(&super->s_reserved_segments, segno, (void *)1, GFP_NOFS);
+       err = wbuf_read(sb, dev_ofs(sb, segno, 0), sizeof(sh), &sh);
+       BUG_ON(err);
+       gc_level = GC_LEVEL(sh.level);
+       logical_segno = be32_to_cpu(sh.segno);
+       if (sh.crc != logfs_crc32(&sh, sizeof(sh), 4)) {
+               logfs_mark_segment_bad(sb, segno);
+               cleaned = -1;
+               goto out;
+       }
+
+       for (seg_ofs = LOGFS_SEGMENT_HEADERSIZE;
+                       seg_ofs + sizeof(oh) < super->s_segsize; ) {
+               ofs = dev_ofs(sb, logical_segno, seg_ofs);
+               err = wbuf_read(sb, dev_ofs(sb, segno, seg_ofs), sizeof(oh),
+                               &oh);
+               BUG_ON(err);
+
+               if (!memchr_inv(&oh, 0xff, sizeof(oh)))
+                       break;
+
+               if (oh.crc != logfs_crc32(&oh, sizeof(oh) - 4, 4)) {
+                       logfs_mark_segment_bad(sb, segno);
+                       cleaned = super->s_segsize - 1;
+                       goto out;
+               }
+
+               ino = be64_to_cpu(oh.ino);
+               bix = be64_to_cpu(oh.bix);
+               len = sizeof(oh) + be16_to_cpu(oh.len);
+               valid = logfs_is_valid_block(sb, ofs, ino, bix, gc_level);
+               if (valid == 1) {
+                       logfs_cleanse_block(sb, ofs, ino, bix, gc_level);
+                       cleaned += len;
+               } else if (valid == 2) {
+                       /* Will be invalid upon journal commit */
+                       cleaned += len;
+               }
+               seg_ofs += len;
+       }
+out:
+       btree_remove32(&super->s_reserved_segments, segno);
+       return cleaned;
+}
+
+static struct gc_candidate *add_list(struct gc_candidate *cand,
+               struct candidate_list *list)
+{
+       struct rb_node **p = &list->rb_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct gc_candidate *cur;
+       int comp;
+
+       cand->list = list;
+       while (*p) {
+               parent = *p;
+               cur = rb_entry(parent, struct gc_candidate, rb_node);
+
+               if (list->sort_by_ec)
+                       comp = cand->erase_count < cur->erase_count;
+               else
+                       comp = cand->valid < cur->valid;
+
+               if (comp)
+                       p = &parent->rb_left;
+               else
+                       p = &parent->rb_right;
+       }
+       rb_link_node(&cand->rb_node, parent, p);
+       rb_insert_color(&cand->rb_node, &list->rb_tree);
+
+       if (list->count <= list->maxcount) {
+               list->count++;
+               return NULL;
+       }
+       cand = rb_entry(rb_last(&list->rb_tree), struct gc_candidate, rb_node);
+       rb_erase(&cand->rb_node, &list->rb_tree);
+       cand->list = NULL;
+       return cand;
+}
+
+static void remove_from_list(struct gc_candidate *cand)
+{
+       struct candidate_list *list = cand->list;
+
+       rb_erase(&cand->rb_node, &list->rb_tree);
+       list->count--;
+}
+
+static void free_candidate(struct super_block *sb, struct gc_candidate *cand)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       btree_remove32(&super->s_cand_tree, cand->segno);
+       kfree(cand);
+}
+
+u32 get_best_cand(struct super_block *sb, struct candidate_list *list, u32 *ec)
+{
+       struct gc_candidate *cand;
+       u32 segno;
+
+       BUG_ON(list->count == 0);
+
+       cand = rb_entry(rb_first(&list->rb_tree), struct gc_candidate, rb_node);
+       remove_from_list(cand);
+       segno = cand->segno;
+       if (ec)
+               *ec = cand->erase_count;
+       free_candidate(sb, cand);
+       return segno;
+}
+
+/*
+ * We have several lists to manage segments with.  The reserve_list is used to
+ * deal with bad blocks.  We try to keep the best (lowest ec) segments on this
+ * list.
+ * The free_list contains free segments for normal usage.  It usually gets the
+ * second pick after the reserve_list.  But when the free_list is running short
+ * it is more important to keep the free_list full than to keep a reserve.
+ *
+ * Segments that are not free are put onto a per-level low_list.  If we have
+ * to run garbage collection, we pick a candidate from there.  All segments on
+ * those lists should have at least some free space so GC will make progress.
+ *
+ * And last we have the ec_list, which is used to pick segments for wear
+ * leveling.
+ *
+ * If all appropriate lists are full, we simply free the candidate and forget
+ * about that segment for a while.  We have better candidates for each purpose.
+ */
+static void __add_candidate(struct super_block *sb, struct gc_candidate *cand)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u32 full = super->s_segsize - LOGFS_SEGMENT_RESERVE;
+
+       if (cand->valid == 0) {
+               /* 100% free segments */
+               log_gc_noisy("add reserve segment %x (ec %x) at %llx\n",
+                               cand->segno, cand->erase_count,
+                               dev_ofs(sb, cand->segno, 0));
+               cand = add_list(cand, &super->s_reserve_list);
+               if (cand) {
+                       log_gc_noisy("add free segment %x (ec %x) at %llx\n",
+                                       cand->segno, cand->erase_count,
+                                       dev_ofs(sb, cand->segno, 0));
+                       cand = add_list(cand, &super->s_free_list);
+               }
+       } else {
+               /* good candidates for Garbage Collection */
+               if (cand->valid < full)
+                       cand = add_list(cand, &super->s_low_list[cand->dist]);
+               /* good candidates for wear leveling,
+                * segments that were recently written get ignored */
+               if (cand)
+                       cand = add_list(cand, &super->s_ec_list);
+       }
+       if (cand)
+               free_candidate(sb, cand);
+}
+
+static int add_candidate(struct super_block *sb, u32 segno, u32 valid, u32 ec,
+               u8 dist)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct gc_candidate *cand;
+
+       cand = kmalloc(sizeof(*cand), GFP_NOFS);
+       if (!cand)
+               return -ENOMEM;
+
+       cand->segno = segno;
+       cand->valid = valid;
+       cand->erase_count = ec;
+       cand->dist = dist;
+
+       btree_insert32(&super->s_cand_tree, segno, cand, GFP_NOFS);
+       __add_candidate(sb, cand);
+       return 0;
+}
+
+static void remove_segment_from_lists(struct super_block *sb, u32 segno)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct gc_candidate *cand;
+
+       cand = btree_lookup32(&super->s_cand_tree, segno);
+       if (cand) {
+               remove_from_list(cand);
+               free_candidate(sb, cand);
+       }
+}
+
+static void scan_segment(struct super_block *sb, u32 segno)
+{
+       u32 valid, ec = 0;
+       gc_level_t gc_level = 0;
+       u8 dist;
+
+       if (segment_is_reserved(sb, segno))
+               return;
+
+       remove_segment_from_lists(sb, segno);
+       valid = logfs_valid_bytes(sb, segno, &ec, &gc_level);
+       if (valid == RESERVED)
+               return;
+
+       dist = root_distance(sb, gc_level);
+       add_candidate(sb, segno, valid, ec, dist);
+}
+
+static struct gc_candidate *first_in_list(struct candidate_list *list)
+{
+       if (list->count == 0)
+               return NULL;
+       return rb_entry(rb_first(&list->rb_tree), struct gc_candidate, rb_node);
+}
+
+/*
+ * Find the best segment for garbage collection.  Main criterion is
+ * the segment requiring the least effort to clean.  Secondary
+ * criterion is to GC on the lowest level available.
+ *
+ * So we search the least effort segment on the lowest level first,
+ * then move up and pick another segment iff is requires significantly
+ * less effort.  Hence the LOGFS_MAX_OBJECTSIZE in the comparison.
+ */
+static struct gc_candidate *get_candidate(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i, max_dist;
+       struct gc_candidate *cand = NULL, *this;
+
+       max_dist = min(no_free_segments(sb), LOGFS_NO_AREAS);
+
+       for (i = max_dist; i >= 0; i--) {
+               this = first_in_list(&super->s_low_list[i]);
+               if (!this)
+                       continue;
+               if (!cand)
+                       cand = this;
+               if (this->valid + LOGFS_MAX_OBJECTSIZE <= cand->valid)
+                       cand = this;
+       }
+       return cand;
+}
+
+static int __logfs_gc_once(struct super_block *sb, struct gc_candidate *cand)
+{
+       struct logfs_super *super = logfs_super(sb);
+       gc_level_t gc_level;
+       u32 cleaned, valid, segno, ec;
+       u8 dist;
+
+       if (!cand) {
+               log_gc("GC attempted, but no candidate found\n");
+               return 0;
+       }
+
+       segno = cand->segno;
+       dist = cand->dist;
+       valid = logfs_valid_bytes(sb, segno, &ec, &gc_level);
+       free_candidate(sb, cand);
+       log_gc("GC segment #%02x at %llx, %x required, %x free, %x valid, %llx free\n",
+                       segno, (u64)segno << super->s_segshift,
+                       dist, no_free_segments(sb), valid,
+                       super->s_free_bytes);
+       cleaned = logfs_gc_segment(sb, segno, dist);
+       log_gc("GC segment #%02x complete - now %x valid\n", segno,
+                       valid - cleaned);
+       BUG_ON(cleaned != valid);
+       return 1;
+}
+
+static int logfs_gc_once(struct super_block *sb)
+{
+       struct gc_candidate *cand;
+
+       cand = get_candidate(sb);
+       if (cand)
+               remove_from_list(cand);
+       return __logfs_gc_once(sb, cand);
+}
+
+/* returns 1 if a wrap occurs, 0 otherwise */
+static int logfs_scan_some(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u32 segno;
+       int i, ret = 0;
+
+       segno = super->s_sweeper;
+       for (i = SCAN_RATIO; i > 0; i--) {
+               segno++;
+               if (segno >= super->s_no_segs) {
+                       segno = 0;
+                       ret = 1;
+                       /* Break out of the loop.  We want to read a single
+                        * block from the segment size on next invocation if
+                        * SCAN_RATIO is set to match block size
+                        */
+                       break;
+               }
+
+               scan_segment(sb, segno);
+       }
+       super->s_sweeper = segno;
+       return ret;
+}
+
+/*
+ * In principle, this function should loop forever, looking for GC candidates
+ * and moving data.  LogFS is designed in such a way that this loop is
+ * guaranteed to terminate.
+ *
+ * Limiting the loop to some iterations serves purely to catch cases when
+ * these guarantees have failed.  An actual endless loop is an obvious bug
+ * and should be reported as such.
+ */
+static void __logfs_gc_pass(struct super_block *sb, int target)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_block *block;
+       int round, progress, last_progress = 0;
+
+       if (no_free_segments(sb) >= target &&
+                       super->s_no_object_aliases < MAX_OBJ_ALIASES)
+               return;
+
+       log_gc("__logfs_gc_pass(%x)\n", target);
+       for (round = 0; round < SCAN_ROUNDS; ) {
+               if (no_free_segments(sb) >= target)
+                       goto write_alias;
+
+               /* Sync in-memory state with on-medium state in case they
+                * diverged */
+               logfs_write_anchor(sb);
+               round += logfs_scan_some(sb);
+               if (no_free_segments(sb) >= target)
+                       goto write_alias;
+               progress = logfs_gc_once(sb);
+               if (progress)
+                       last_progress = round;
+               else if (round - last_progress > 2)
+                       break;
+               continue;
+
+               /*
+                * The goto logic is nasty, I just don't know a better way to
+                * code it.  GC is supposed to ensure two things:
+                * 1. Enough free segments are available.
+                * 2. The number of aliases is bounded.
+                * When 1. is achieved, we take a look at 2. and write back
+                * some alias-containing blocks, if necessary.  However, after
+                * each such write we need to go back to 1., as writes can
+                * consume free segments.
+                */
+write_alias:
+               if (super->s_no_object_aliases < MAX_OBJ_ALIASES)
+                       return;
+               if (list_empty(&super->s_object_alias)) {
+                       /* All aliases are still in btree */
+                       return;
+               }
+               log_gc("Write back one alias\n");
+               block = list_entry(super->s_object_alias.next,
+                               struct logfs_block, alias_list);
+               block->ops->write_block(block);
+               /*
+                * To round off the nasty goto logic, we reset round here.  It
+                * is a safety-net for GC not making any progress and limited
+                * to something reasonably small.  If incremented it for every
+                * single alias, the loop could terminate rather quickly.
+                */
+               round = 0;
+       }
+       LOGFS_BUG(sb);
+}
+
+static int wl_ratelimit(struct super_block *sb, u64 *next_event)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       if (*next_event < super->s_gec) {
+               *next_event = super->s_gec + WL_RATELIMIT;
+               return 0;
+       }
+       return 1;
+}
+
+static void logfs_wl_pass(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct gc_candidate *wl_cand, *free_cand;
+
+       if (wl_ratelimit(sb, &super->s_wl_gec_ostore))
+               return;
+
+       wl_cand = first_in_list(&super->s_ec_list);
+       if (!wl_cand)
+               return;
+       free_cand = first_in_list(&super->s_free_list);
+       if (!free_cand)
+               return;
+
+       if (wl_cand->erase_count < free_cand->erase_count + WL_DELTA) {
+               remove_from_list(wl_cand);
+               __logfs_gc_once(sb, wl_cand);
+       }
+}
+
+/*
+ * The journal needs wear leveling as well.  But moving the journal is an
+ * expensive operation so we try to avoid it as much as possible.  And if we
+ * have to do it, we move the whole journal, not individual segments.
+ *
+ * Ratelimiting is not strictly necessary here, it mainly serves to avoid the
+ * calculations.  First we check whether moving the journal would be a
+ * significant improvement.  That means that a) the current journal segments
+ * have more wear than the future journal segments and b) the current journal
+ * segments have more wear than normal ostore segments.
+ * Rationale for b) is that we don't have to move the journal if it is aging
+ * less than the ostore, even if the reserve segments age even less (they are
+ * excluded from wear leveling, after all).
+ * Next we check that the superblocks have less wear than the journal.  Since
+ * moving the journal requires writing the superblocks, we have to protect the
+ * superblocks even more than the journal.
+ *
+ * Also we double the acceptable wear difference, compared to ostore wear
+ * leveling.  Journal data is read and rewritten rapidly, comparatively.  So
+ * soft errors have much less time to accumulate and we allow the journal to
+ * be a bit worse than the ostore.
+ */
+static void logfs_journal_wl_pass(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct gc_candidate *cand;
+       u32 min_journal_ec = -1, max_reserve_ec = 0;
+       int i;
+
+       if (wl_ratelimit(sb, &super->s_wl_gec_journal))
+               return;
+
+       if (super->s_reserve_list.count < super->s_no_journal_segs) {
+               /* Reserve is not full enough to move complete journal */
+               return;
+       }
+
+       journal_for_each(i)
+               if (super->s_journal_seg[i])
+                       min_journal_ec = min(min_journal_ec,
+                                       super->s_journal_ec[i]);
+       cand = rb_entry(rb_first(&super->s_free_list.rb_tree),
+                       struct gc_candidate, rb_node);
+       max_reserve_ec = cand->erase_count;
+       for (i = 0; i < 2; i++) {
+               struct logfs_segment_entry se;
+               u32 segno = seg_no(sb, super->s_sb_ofs[i]);
+               u32 ec;
+
+               logfs_get_segment_entry(sb, segno, &se);
+               ec = be32_to_cpu(se.ec_level) >> 4;
+               max_reserve_ec = max(max_reserve_ec, ec);
+       }
+
+       if (min_journal_ec > max_reserve_ec + 2 * WL_DELTA) {
+               do_logfs_journal_wl_pass(sb);
+       }
+}
+
+void logfs_gc_pass(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       //BUG_ON(mutex_trylock(&logfs_super(sb)->s_w_mutex));
+       /* Write journal before free space is getting saturated with dirty
+        * objects.
+        */
+       if (super->s_dirty_used_bytes + super->s_dirty_free_bytes
+                       + LOGFS_MAX_OBJECTSIZE >= super->s_free_bytes)
+               logfs_write_anchor(sb);
+       __logfs_gc_pass(sb, super->s_total_levels);
+       logfs_wl_pass(sb);
+       logfs_journal_wl_pass(sb);
+}
+
+static int check_area(struct super_block *sb, int i)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area = super->s_area[i];
+       struct logfs_object_header oh;
+       u32 segno = area->a_segno;
+       u32 ofs = area->a_used_bytes;
+       __be32 crc;
+       int err;
+
+       if (!area->a_is_open)
+               return 0;
+
+       for (ofs = area->a_used_bytes;
+            ofs <= super->s_segsize - sizeof(oh);
+            ofs += (u32)be16_to_cpu(oh.len) + sizeof(oh)) {
+               err = wbuf_read(sb, dev_ofs(sb, segno, ofs), sizeof(oh), &oh);
+               if (err)
+                       return err;
+
+               if (!memchr_inv(&oh, 0xff, sizeof(oh)))
+                       break;
+
+               crc = logfs_crc32(&oh, sizeof(oh) - 4, 4);
+               if (crc != oh.crc) {
+                       printk(KERN_INFO "interrupted header at %llx\n",
+                                       dev_ofs(sb, segno, ofs));
+                       return 0;
+               }
+       }
+       if (ofs != area->a_used_bytes) {
+               printk(KERN_INFO "%x bytes unaccounted data found at %llx\n",
+                               ofs - area->a_used_bytes,
+                               dev_ofs(sb, segno, area->a_used_bytes));
+               area->a_used_bytes = ofs;
+       }
+       return 0;
+}
+
+int logfs_check_areas(struct super_block *sb)
+{
+       int i, err;
+
+       for_each_area(i) {
+               err = check_area(sb, i);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static void logfs_init_candlist(struct candidate_list *list, int maxcount,
+               int sort_by_ec)
+{
+       list->count = 0;
+       list->maxcount = maxcount;
+       list->sort_by_ec = sort_by_ec;
+       list->rb_tree = RB_ROOT;
+}
+
+int logfs_init_gc(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i;
+
+       btree_init_mempool32(&super->s_cand_tree, super->s_btree_pool);
+       logfs_init_candlist(&super->s_free_list, LIST_SIZE + SCAN_RATIO, 1);
+       logfs_init_candlist(&super->s_reserve_list,
+                       super->s_bad_seg_reserve, 1);
+       for_each_area(i)
+               logfs_init_candlist(&super->s_low_list[i], LIST_SIZE, 0);
+       logfs_init_candlist(&super->s_ec_list, LIST_SIZE, 1);
+       return 0;
+}
+
+static void logfs_cleanup_list(struct super_block *sb,
+               struct candidate_list *list)
+{
+       struct gc_candidate *cand;
+
+       while (list->count) {
+               cand = rb_entry(list->rb_tree.rb_node, struct gc_candidate,
+                               rb_node);
+               remove_from_list(cand);
+               free_candidate(sb, cand);
+       }
+       BUG_ON(list->rb_tree.rb_node);
+}
+
+void logfs_cleanup_gc(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i;
+
+       if (!super->s_free_list.count)
+               return;
+
+       /*
+        * FIXME: The btree may still contain a single empty node.  So we
+        * call the grim visitor to clean up that mess.  Btree code should
+        * do it for us, really.
+        */
+       btree_grim_visitor32(&super->s_cand_tree, 0, NULL);
+       logfs_cleanup_list(sb, &super->s_free_list);
+       logfs_cleanup_list(sb, &super->s_reserve_list);
+       for_each_area(i)
+               logfs_cleanup_list(sb, &super->s_low_list[i]);
+       logfs_cleanup_list(sb, &super->s_ec_list);
+}
diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c
new file mode 100644 (file)
index 0000000..33ec1ae
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * fs/logfs/inode.c    - inode handling code
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+#include <linux/writeback.h>
+#include <linux/backing-dev.h>
+
+/*
+ * How soon to reuse old inode numbers?  LogFS doesn't store deleted inodes
+ * on the medium.  It therefore also lacks a method to store the previous
+ * generation number for deleted inodes.  Instead a single generation number
+ * is stored which will be used for new inodes.  Being just a 32bit counter,
+ * this can obvious wrap relatively quickly.  So we only reuse inodes if we
+ * know that a fair number of inodes can be created before we have to increment
+ * the generation again - effectively adding some bits to the counter.
+ * But being too aggressive here means we keep a very large and very sparse
+ * inode file, wasting space on indirect blocks.
+ * So what is a good value?  Beats me.  64k seems moderately bad on both
+ * fronts, so let's use that for now...
+ *
+ * NFS sucks, as everyone already knows.
+ */
+#define INOS_PER_WRAP (0x10000)
+
+/*
+ * Logfs' requirement to read inodes for garbage collection makes life a bit
+ * harder.  GC may have to read inodes that are in I_FREEING state, when they
+ * are being written out - and waiting for GC to make progress, naturally.
+ *
+ * So we cannot just call iget() or some variant of it, but first have to check
+ * wether the inode in question might be in I_FREEING state.  Therefore we
+ * maintain our own per-sb list of "almost deleted" inodes and check against
+ * that list first.  Normally this should be at most 1-2 entries long.
+ *
+ * Also, inodes have logfs-specific reference counting on top of what the vfs
+ * does.  When .destroy_inode is called, normally the reference count will drop
+ * to zero and the inode gets deleted.  But if GC accessed the inode, its
+ * refcount will remain nonzero and final deletion will have to wait.
+ *
+ * As a result we have two sets of functions to get/put inodes:
+ * logfs_safe_iget/logfs_safe_iput     - safe to call from GC context
+ * logfs_iget/iput                     - normal version
+ */
+static struct kmem_cache *logfs_inode_cache;
+
+static DEFINE_SPINLOCK(logfs_inode_lock);
+
+static void logfs_inode_setops(struct inode *inode)
+{
+       switch (inode->i_mode & S_IFMT) {
+       case S_IFDIR:
+               inode->i_op = &logfs_dir_iops;
+               inode->i_fop = &logfs_dir_fops;
+               inode->i_mapping->a_ops = &logfs_reg_aops;
+               break;
+       case S_IFREG:
+               inode->i_op = &logfs_reg_iops;
+               inode->i_fop = &logfs_reg_fops;
+               inode->i_mapping->a_ops = &logfs_reg_aops;
+               break;
+       case S_IFLNK:
+               inode->i_op = &logfs_symlink_iops;
+               inode->i_mapping->a_ops = &logfs_reg_aops;
+               break;
+       case S_IFSOCK:  /* fall through */
+       case S_IFBLK:   /* fall through */
+       case S_IFCHR:   /* fall through */
+       case S_IFIFO:
+               init_special_inode(inode, inode->i_mode, inode->i_rdev);
+               break;
+       default:
+               BUG();
+       }
+}
+
+static struct inode *__logfs_iget(struct super_block *sb, ino_t ino)
+{
+       struct inode *inode = iget_locked(sb, ino);
+       int err;
+
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+       if (!(inode->i_state & I_NEW))
+               return inode;
+
+       err = logfs_read_inode(inode);
+       if (err || inode->i_nlink == 0) {
+               /* inode->i_nlink == 0 can be true when called from
+                * block validator */
+               /* set i_nlink to 0 to prevent caching */
+               inode->i_nlink = 0;
+               logfs_inode(inode)->li_flags |= LOGFS_IF_ZOMBIE;
+               iget_failed(inode);
+               if (!err)
+                       err = -ENOENT;
+               return ERR_PTR(err);
+       }
+
+       logfs_inode_setops(inode);
+       unlock_new_inode(inode);
+       return inode;
+}
+
+struct inode *logfs_iget(struct super_block *sb, ino_t ino)
+{
+       BUG_ON(ino == LOGFS_INO_MASTER);
+       BUG_ON(ino == LOGFS_INO_SEGFILE);
+       return __logfs_iget(sb, ino);
+}
+
+/*
+ * is_cached is set to 1 if we hand out a cached inode, 0 otherwise.
+ * this allows logfs_iput to do the right thing later
+ */
+struct inode *logfs_safe_iget(struct super_block *sb, ino_t ino, int *is_cached)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_inode *li;
+
+       if (ino == LOGFS_INO_MASTER)
+               return super->s_master_inode;
+       if (ino == LOGFS_INO_SEGFILE)
+               return super->s_segfile_inode;
+
+       spin_lock(&logfs_inode_lock);
+       list_for_each_entry(li, &super->s_freeing_list, li_freeing_list)
+               if (li->vfs_inode.i_ino == ino) {
+                       li->li_refcount++;
+                       spin_unlock(&logfs_inode_lock);
+                       *is_cached = 1;
+                       return &li->vfs_inode;
+               }
+       spin_unlock(&logfs_inode_lock);
+
+       *is_cached = 0;
+       return __logfs_iget(sb, ino);
+}
+
+static void __logfs_destroy_inode(struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       BUG_ON(li->li_block);
+       list_del(&li->li_freeing_list);
+       kmem_cache_free(logfs_inode_cache, li);
+}
+
+static void logfs_destroy_inode(struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       BUG_ON(list_empty(&li->li_freeing_list));
+       spin_lock(&logfs_inode_lock);
+       li->li_refcount--;
+       if (li->li_refcount == 0)
+               __logfs_destroy_inode(inode);
+       spin_unlock(&logfs_inode_lock);
+}
+
+void logfs_safe_iput(struct inode *inode, int is_cached)
+{
+       if (inode->i_ino == LOGFS_INO_MASTER)
+               return;
+       if (inode->i_ino == LOGFS_INO_SEGFILE)
+               return;
+
+       if (is_cached) {
+               logfs_destroy_inode(inode);
+               return;
+       }
+
+       iput(inode);
+}
+
+static void logfs_init_inode(struct super_block *sb, struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       int i;
+
+       li->li_flags    = 0;
+       li->li_height   = 0;
+       li->li_used_bytes = 0;
+       li->li_block    = NULL;
+       inode->i_uid    = 0;
+       inode->i_gid    = 0;
+       inode->i_size   = 0;
+       inode->i_blocks = 0;
+       inode->i_ctime  = CURRENT_TIME;
+       inode->i_mtime  = CURRENT_TIME;
+       inode->i_nlink  = 1;
+       INIT_LIST_HEAD(&li->li_freeing_list);
+
+       for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
+               li->li_data[i] = 0;
+
+       return;
+}
+
+static struct inode *logfs_alloc_inode(struct super_block *sb)
+{
+       struct logfs_inode *li;
+
+       li = kmem_cache_alloc(logfs_inode_cache, GFP_NOFS);
+       if (!li)
+               return NULL;
+       logfs_init_inode(sb, &li->vfs_inode);
+       return &li->vfs_inode;
+}
+
+/*
+ * In logfs inodes are written to an inode file.  The inode file, like any
+ * other file, is managed with a inode.  The inode file's inode, aka master
+ * inode, requires special handling in several respects.  First, it cannot be
+ * written to the inode file, so it is stored in the journal instead.
+ *
+ * Secondly, this inode cannot be written back and destroyed before all other
+ * inodes have been written.  The ordering is important.  Linux' VFS is happily
+ * unaware of the ordering constraint and would ordinarily destroy the master
+ * inode at umount time while other inodes are still in use and dirty.  Not
+ * good.
+ *
+ * So logfs makes sure the master inode is not written until all other inodes
+ * have been destroyed.  Sadly, this method has another side-effect.  The VFS
+ * will notice one remaining inode and print a frightening warning message.
+ * Worse, it is impossible to judge whether such a warning was caused by the
+ * master inode or any other inodes have leaked as well.
+ *
+ * Our attempt of solving this is with logfs_new_meta_inode() below.  Its
+ * purpose is to create a new inode that will not trigger the warning if such
+ * an inode is still in use.  An ugly hack, no doubt.  Suggections for
+ * improvement are welcome.
+ */
+struct inode *logfs_new_meta_inode(struct super_block *sb, u64 ino)
+{
+       struct inode *inode;
+
+       inode = logfs_alloc_inode(sb);
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+
+       inode->i_mode = S_IFREG;
+       inode->i_ino = ino;
+       inode->i_sb = sb;
+
+       /* This is a blatant copy of alloc_inode code.  We'd need alloc_inode
+        * to be nonstatic, alas. */
+       {
+               struct address_space * const mapping = &inode->i_data;
+
+               mapping->a_ops = &logfs_reg_aops;
+               mapping->host = inode;
+               mapping->flags = 0;
+               mapping_set_gfp_mask(mapping, GFP_NOFS);
+               mapping->assoc_mapping = NULL;
+               mapping->backing_dev_info = &default_backing_dev_info;
+               inode->i_mapping = mapping;
+               inode->i_nlink = 1;
+       }
+
+       return inode;
+}
+
+struct inode *logfs_read_meta_inode(struct super_block *sb, u64 ino)
+{
+       struct inode *inode;
+       int err;
+
+       inode = logfs_new_meta_inode(sb, ino);
+       if (IS_ERR(inode))
+               return inode;
+
+       err = logfs_read_inode(inode);
+       if (err) {
+               destroy_meta_inode(inode);
+               return ERR_PTR(err);
+       }
+       logfs_inode_setops(inode);
+       return inode;
+}
+
+static int logfs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       int ret;
+       long flags = WF_LOCK;
+
+       /* Can only happen if creat() failed.  Safe to skip. */
+       if (logfs_inode(inode)->li_flags & LOGFS_IF_STILLBORN)
+               return 0;
+
+       ret = __logfs_write_inode(inode, flags);
+       LOGFS_BUG_ON(ret, inode->i_sb);
+       return ret;
+}
+
+void destroy_meta_inode(struct inode *inode)
+{
+       if (inode) {
+               if (inode->i_data.nrpages)
+                       truncate_inode_pages(&inode->i_data, 0);
+               logfs_clear_inode(inode);
+               kmem_cache_free(logfs_inode_cache, logfs_inode(inode));
+       }
+}
+
+/* called with inode_lock held */
+static void logfs_drop_inode(struct inode *inode)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+       struct logfs_inode *li = logfs_inode(inode);
+
+       spin_lock(&logfs_inode_lock);
+       list_move(&li->li_freeing_list, &super->s_freeing_list);
+       spin_unlock(&logfs_inode_lock);
+       generic_drop_inode(inode);
+}
+
+static void logfs_set_ino_generation(struct super_block *sb,
+               struct inode *inode)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u64 ino;
+
+       mutex_lock(&super->s_journal_mutex);
+       ino = logfs_seek_hole(super->s_master_inode, super->s_last_ino);
+       super->s_last_ino = ino;
+       super->s_inos_till_wrap--;
+       if (super->s_inos_till_wrap < 0) {
+               super->s_last_ino = LOGFS_RESERVED_INOS;
+               super->s_generation++;
+               super->s_inos_till_wrap = INOS_PER_WRAP;
+       }
+       inode->i_ino = ino;
+       inode->i_generation = super->s_generation;
+       mutex_unlock(&super->s_journal_mutex);
+}
+
+struct inode *logfs_new_inode(struct inode *dir, int mode)
+{
+       struct super_block *sb = dir->i_sb;
+       struct inode *inode;
+
+       inode = new_inode(sb);
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+
+       logfs_init_inode(sb, inode);
+
+       /* inherit parent flags */
+       logfs_inode(inode)->li_flags |=
+               logfs_inode(dir)->li_flags & LOGFS_FL_INHERITED;
+
+       inode->i_mode = mode;
+       logfs_set_ino_generation(sb, inode);
+
+       inode->i_uid = current_fsuid();
+       inode->i_gid = current_fsgid();
+       if (dir->i_mode & S_ISGID) {
+               inode->i_gid = dir->i_gid;
+               if (S_ISDIR(mode))
+                       inode->i_mode |= S_ISGID;
+       }
+
+       logfs_inode_setops(inode);
+       insert_inode_hash(inode);
+
+       return inode;
+}
+
+static void logfs_init_once(void *_li)
+{
+       struct logfs_inode *li = _li;
+       int i;
+
+       li->li_flags = 0;
+       li->li_used_bytes = 0;
+       li->li_refcount = 1;
+       for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
+               li->li_data[i] = 0;
+       inode_init_once(&li->vfs_inode);
+}
+
+static int logfs_sync_fs(struct super_block *sb, int wait)
+{
+       /* FIXME: write anchor */
+       logfs_super(sb)->s_devops->sync(sb);
+       return 0;
+}
+
+const struct super_operations logfs_super_operations = {
+       .alloc_inode    = logfs_alloc_inode,
+       .clear_inode    = logfs_clear_inode,
+       .delete_inode   = logfs_delete_inode,
+       .destroy_inode  = logfs_destroy_inode,
+       .drop_inode     = logfs_drop_inode,
+       .write_inode    = logfs_write_inode,
+       .statfs         = logfs_statfs,
+       .sync_fs        = logfs_sync_fs,
+};
+
+int logfs_init_inode_cache(void)
+{
+       logfs_inode_cache = kmem_cache_create("logfs_inode_cache",
+                       sizeof(struct logfs_inode), 0, SLAB_RECLAIM_ACCOUNT,
+                       logfs_init_once);
+       if (!logfs_inode_cache)
+               return -ENOMEM;
+       return 0;
+}
+
+void logfs_destroy_inode_cache(void)
+{
+       kmem_cache_destroy(logfs_inode_cache);
+}
diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c
new file mode 100644 (file)
index 0000000..6ad30a4
--- /dev/null
@@ -0,0 +1,883 @@
+/*
+ * fs/logfs/journal.c  - journal handling code
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ */
+#include "logfs.h"
+
+static void logfs_calc_free(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u64 reserve, no_segs = super->s_no_segs;
+       s64 free;
+       int i;
+
+       /* superblock segments */
+       no_segs -= 2;
+       super->s_no_journal_segs = 0;
+       /* journal */
+       journal_for_each(i)
+               if (super->s_journal_seg[i]) {
+                       no_segs--;
+                       super->s_no_journal_segs++;
+               }
+
+       /* open segments plus one extra per level for GC */
+       no_segs -= 2 * super->s_total_levels;
+
+       free = no_segs * (super->s_segsize - LOGFS_SEGMENT_RESERVE);
+       free -= super->s_used_bytes;
+       /* just a bit extra */
+       free -= super->s_total_levels * 4096;
+
+       /* Bad blocks are 'paid' for with speed reserve - the filesystem
+        * simply gets slower as bad blocks accumulate.  Until the bad blocks
+        * exceed the speed reserve - then the filesystem gets smaller.
+        */
+       reserve = super->s_bad_segments + super->s_bad_seg_reserve;
+       reserve *= super->s_segsize - LOGFS_SEGMENT_RESERVE;
+       reserve = max(reserve, super->s_speed_reserve);
+       free -= reserve;
+       if (free < 0)
+               free = 0;
+
+       super->s_free_bytes = free;
+}
+
+static void reserve_sb_and_journal(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct btree_head32 *head = &super->s_reserved_segments;
+       int i, err;
+
+       err = btree_insert32(head, seg_no(sb, super->s_sb_ofs[0]), (void *)1,
+                       GFP_KERNEL);
+       BUG_ON(err);
+
+       err = btree_insert32(head, seg_no(sb, super->s_sb_ofs[1]), (void *)1,
+                       GFP_KERNEL);
+       BUG_ON(err);
+
+       journal_for_each(i) {
+               if (!super->s_journal_seg[i])
+                       continue;
+               err = btree_insert32(head, super->s_journal_seg[i], (void *)1,
+                               GFP_KERNEL);
+               BUG_ON(err);
+       }
+}
+
+static void read_dynsb(struct super_block *sb,
+               struct logfs_je_dynsb *dynsb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       super->s_gec            = be64_to_cpu(dynsb->ds_gec);
+       super->s_sweeper        = be64_to_cpu(dynsb->ds_sweeper);
+       super->s_victim_ino     = be64_to_cpu(dynsb->ds_victim_ino);
+       super->s_rename_dir     = be64_to_cpu(dynsb->ds_rename_dir);
+       super->s_rename_pos     = be64_to_cpu(dynsb->ds_rename_pos);
+       super->s_used_bytes     = be64_to_cpu(dynsb->ds_used_bytes);
+       super->s_generation     = be32_to_cpu(dynsb->ds_generation);
+}
+
+static void read_anchor(struct super_block *sb,
+               struct logfs_je_anchor *da)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *inode = super->s_master_inode;
+       struct logfs_inode *li = logfs_inode(inode);
+       int i;
+
+       super->s_last_ino = be64_to_cpu(da->da_last_ino);
+       li->li_flags    = 0;
+       li->li_height   = da->da_height;
+       i_size_write(inode, be64_to_cpu(da->da_size));
+       li->li_used_bytes = be64_to_cpu(da->da_used_bytes);
+
+       for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
+               li->li_data[i] = be64_to_cpu(da->da_data[i]);
+}
+
+static void read_erasecount(struct super_block *sb,
+               struct logfs_je_journal_ec *ec)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i;
+
+       journal_for_each(i)
+               super->s_journal_ec[i] = be32_to_cpu(ec->ec[i]);
+}
+
+static int read_area(struct super_block *sb, struct logfs_je_area *a)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area = super->s_area[a->gc_level];
+       u64 ofs;
+       u32 writemask = ~(super->s_writesize - 1);
+
+       if (a->gc_level >= LOGFS_NO_AREAS)
+               return -EIO;
+       if (a->vim != VIM_DEFAULT)
+               return -EIO; /* TODO: close area and continue */
+
+       area->a_used_bytes = be32_to_cpu(a->used_bytes);
+       area->a_written_bytes = area->a_used_bytes & writemask;
+       area->a_segno = be32_to_cpu(a->segno);
+       if (area->a_segno)
+               area->a_is_open = 1;
+
+       ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
+       if (super->s_writesize > 1)
+               logfs_buf_recover(area, ofs, a + 1, super->s_writesize);
+       else
+               logfs_buf_recover(area, ofs, NULL, 0);
+       return 0;
+}
+
+static void *unpack(void *from, void *to)
+{
+       struct logfs_journal_header *jh = from;
+       void *data = from + sizeof(struct logfs_journal_header);
+       int err;
+       size_t inlen, outlen;
+
+       inlen = be16_to_cpu(jh->h_len);
+       outlen = be16_to_cpu(jh->h_datalen);
+
+       if (jh->h_compr == COMPR_NONE)
+               memcpy(to, data, inlen);
+       else {
+               err = logfs_uncompress(data, to, inlen, outlen);
+               BUG_ON(err);
+       }
+       return to;
+}
+
+static int __read_je_header(struct super_block *sb, u64 ofs,
+               struct logfs_journal_header *jh)
+{
+       struct logfs_super *super = logfs_super(sb);
+       size_t bufsize = max_t(size_t, sb->s_blocksize, super->s_writesize)
+               + MAX_JOURNAL_HEADER;
+       u16 type, len, datalen;
+       int err;
+
+       /* read header only */
+       err = wbuf_read(sb, ofs, sizeof(*jh), jh);
+       if (err)
+               return err;
+       type = be16_to_cpu(jh->h_type);
+       len = be16_to_cpu(jh->h_len);
+       datalen = be16_to_cpu(jh->h_datalen);
+       if (len > sb->s_blocksize)
+               return -EIO;
+       if ((type < JE_FIRST) || (type > JE_LAST))
+               return -EIO;
+       if (datalen > bufsize)
+               return -EIO;
+       return 0;
+}
+
+static int __read_je_payload(struct super_block *sb, u64 ofs,
+               struct logfs_journal_header *jh)
+{
+       u16 len;
+       int err;
+
+       len = be16_to_cpu(jh->h_len);
+       err = wbuf_read(sb, ofs + sizeof(*jh), len, jh + 1);
+       if (err)
+               return err;
+       if (jh->h_crc != logfs_crc32(jh, len + sizeof(*jh), 4)) {
+               /* Old code was confused.  It forgot about the header length
+                * and stopped calculating the crc 16 bytes before the end
+                * of data - ick!
+                * FIXME: Remove this hack once the old code is fixed.
+                */
+               if (jh->h_crc == logfs_crc32(jh, len, 4))
+                       WARN_ON_ONCE(1);
+               else
+                       return -EIO;
+       }
+       return 0;
+}
+
+/*
+ * jh needs to be large enough to hold the complete entry, not just the header
+ */
+static int __read_je(struct super_block *sb, u64 ofs,
+               struct logfs_journal_header *jh)
+{
+       int err;
+
+       err = __read_je_header(sb, ofs, jh);
+       if (err)
+               return err;
+       return __read_je_payload(sb, ofs, jh);
+}
+
+static int read_je(struct super_block *sb, u64 ofs)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_journal_header *jh = super->s_compressed_je;
+       void *scratch = super->s_je;
+       u16 type, datalen;
+       int err;
+
+       err = __read_je(sb, ofs, jh);
+       if (err)
+               return err;
+       type = be16_to_cpu(jh->h_type);
+       datalen = be16_to_cpu(jh->h_datalen);
+
+       switch (type) {
+       case JE_DYNSB:
+               read_dynsb(sb, unpack(jh, scratch));
+               break;
+       case JE_ANCHOR:
+               read_anchor(sb, unpack(jh, scratch));
+               break;
+       case JE_ERASECOUNT:
+               read_erasecount(sb, unpack(jh, scratch));
+               break;
+       case JE_AREA:
+               read_area(sb, unpack(jh, scratch));
+               break;
+       case JE_OBJ_ALIAS:
+               err = logfs_load_object_aliases(sb, unpack(jh, scratch),
+                               datalen);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -EIO;
+       }
+       return err;
+}
+
+static int logfs_read_segment(struct super_block *sb, u32 segno)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_journal_header *jh = super->s_compressed_je;
+       u64 ofs, seg_ofs = dev_ofs(sb, segno, 0);
+       u32 h_ofs, last_ofs = 0;
+       u16 len, datalen, last_len = 0;
+       int i, err;
+
+       /* search for most recent commit */
+       for (h_ofs = 0; h_ofs < super->s_segsize; h_ofs += sizeof(*jh)) {
+               ofs = seg_ofs + h_ofs;
+               err = __read_je_header(sb, ofs, jh);
+               if (err)
+                       continue;
+               if (jh->h_type != cpu_to_be16(JE_COMMIT))
+                       continue;
+               err = __read_je_payload(sb, ofs, jh);
+               if (err)
+                       continue;
+               len = be16_to_cpu(jh->h_len);
+               datalen = be16_to_cpu(jh->h_datalen);
+               if ((datalen > sizeof(super->s_je_array)) ||
+                               (datalen % sizeof(__be64)))
+                       continue;
+               last_ofs = h_ofs;
+               last_len = datalen;
+               h_ofs += ALIGN(len, sizeof(*jh)) - sizeof(*jh);
+       }
+       /* read commit */
+       if (last_ofs == 0)
+               return -ENOENT;
+       ofs = seg_ofs + last_ofs;
+       log_journal("Read commit from %llx\n", ofs);
+       err = __read_je(sb, ofs, jh);
+       BUG_ON(err); /* We should have caught it in the scan loop already */
+       if (err)
+               return err;
+       /* uncompress */
+       unpack(jh, super->s_je_array);
+       super->s_no_je = last_len / sizeof(__be64);
+       /* iterate over array */
+       for (i = 0; i < super->s_no_je; i++) {
+               err = read_je(sb, be64_to_cpu(super->s_je_array[i]));
+               if (err)
+                       return err;
+       }
+       super->s_journal_area->a_segno = segno;
+       return 0;
+}
+
+static u64 read_gec(struct super_block *sb, u32 segno)
+{
+       struct logfs_segment_header sh;
+       __be32 crc;
+       int err;
+
+       if (!segno)
+               return 0;
+       err = wbuf_read(sb, dev_ofs(sb, segno, 0), sizeof(sh), &sh);
+       if (err)
+               return 0;
+       crc = logfs_crc32(&sh, sizeof(sh), 4);
+       if (crc != sh.crc) {
+               WARN_ON(sh.gec != cpu_to_be64(0xffffffffffffffffull));
+               /* Most likely it was just erased */
+               return 0;
+       }
+       return be64_to_cpu(sh.gec);
+}
+
+static int logfs_read_journal(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u64 gec[LOGFS_JOURNAL_SEGS], max;
+       u32 segno;
+       int i, max_i;
+
+       max = 0;
+       max_i = -1;
+       journal_for_each(i) {
+               segno = super->s_journal_seg[i];
+               gec[i] = read_gec(sb, super->s_journal_seg[i]);
+               if (gec[i] > max) {
+                       max = gec[i];
+                       max_i = i;
+               }
+       }
+       if (max_i == -1)
+               return -EIO;
+       /* FIXME: Try older segments in case of error */
+       return logfs_read_segment(sb, super->s_journal_seg[max_i]);
+}
+
+/*
+ * First search the current segment (outer loop), then pick the next segment
+ * in the array, skipping any zero entries (inner loop).
+ */
+static void journal_get_free_segment(struct logfs_area *area)
+{
+       struct logfs_super *super = logfs_super(area->a_sb);
+       int i;
+
+       journal_for_each(i) {
+               if (area->a_segno != super->s_journal_seg[i])
+                       continue;
+
+               do {
+                       i++;
+                       if (i == LOGFS_JOURNAL_SEGS)
+                               i = 0;
+               } while (!super->s_journal_seg[i]);
+
+               area->a_segno = super->s_journal_seg[i];
+               area->a_erase_count = ++(super->s_journal_ec[i]);
+               log_journal("Journal now at %x (ec %x)\n", area->a_segno,
+                               area->a_erase_count);
+               return;
+       }
+       BUG();
+}
+
+static void journal_get_erase_count(struct logfs_area *area)
+{
+       /* erase count is stored globally and incremented in
+        * journal_get_free_segment() - nothing to do here */
+}
+
+static int journal_erase_segment(struct logfs_area *area)
+{
+       struct super_block *sb = area->a_sb;
+       struct logfs_segment_header sh;
+       u64 ofs;
+       int err;
+
+       err = logfs_erase_segment(sb, area->a_segno, 1);
+       if (err)
+               return err;
+
+       sh.pad = 0;
+       sh.type = SEG_JOURNAL;
+       sh.level = 0;
+       sh.segno = cpu_to_be32(area->a_segno);
+       sh.ec = cpu_to_be32(area->a_erase_count);
+       sh.gec = cpu_to_be64(logfs_super(sb)->s_gec);
+       sh.crc = logfs_crc32(&sh, sizeof(sh), 4);
+
+       /* This causes a bug in segment.c.  Not yet. */
+       //logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count, 0);
+
+       ofs = dev_ofs(sb, area->a_segno, 0);
+       area->a_used_bytes = ALIGN(sizeof(sh), 16);
+       logfs_buf_write(area, ofs, &sh, sizeof(sh));
+       return 0;
+}
+
+static size_t __logfs_write_header(struct logfs_super *super,
+               struct logfs_journal_header *jh, size_t len, size_t datalen,
+               u16 type, u8 compr)
+{
+       jh->h_len       = cpu_to_be16(len);
+       jh->h_type      = cpu_to_be16(type);
+       jh->h_datalen   = cpu_to_be16(datalen);
+       jh->h_compr     = compr;
+       jh->h_pad[0]    = 'H';
+       jh->h_pad[1]    = 'E';
+       jh->h_pad[2]    = 'A';
+       jh->h_pad[3]    = 'D';
+       jh->h_pad[4]    = 'R';
+       jh->h_crc       = logfs_crc32(jh, len + sizeof(*jh), 4);
+       return ALIGN(len, 16) + sizeof(*jh);
+}
+
+static size_t logfs_write_header(struct logfs_super *super,
+               struct logfs_journal_header *jh, size_t datalen, u16 type)
+{
+       size_t len = datalen;
+
+       return __logfs_write_header(super, jh, len, datalen, type, COMPR_NONE);
+}
+
+static inline size_t logfs_journal_erasecount_size(struct logfs_super *super)
+{
+       return LOGFS_JOURNAL_SEGS * sizeof(__be32);
+}
+
+static void *logfs_write_erasecount(struct super_block *sb, void *_ec,
+               u16 *type, size_t *len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_je_journal_ec *ec = _ec;
+       int i;
+
+       journal_for_each(i)
+               ec->ec[i] = cpu_to_be32(super->s_journal_ec[i]);
+       *type = JE_ERASECOUNT;
+       *len = logfs_journal_erasecount_size(super);
+       return ec;
+}
+
+static void account_shadow(void *_shadow, unsigned long _sb, u64 ignore,
+               size_t ignore2)
+{
+       struct logfs_shadow *shadow = _shadow;
+       struct super_block *sb = (void *)_sb;
+       struct logfs_super *super = logfs_super(sb);
+
+       /* consume new space */
+       super->s_free_bytes       -= shadow->new_len;
+       super->s_used_bytes       += shadow->new_len;
+       super->s_dirty_used_bytes -= shadow->new_len;
+
+       /* free up old space */
+       super->s_free_bytes       += shadow->old_len;
+       super->s_used_bytes       -= shadow->old_len;
+       super->s_dirty_free_bytes -= shadow->old_len;
+
+       logfs_set_segment_used(sb, shadow->old_ofs, -shadow->old_len);
+       logfs_set_segment_used(sb, shadow->new_ofs, shadow->new_len);
+
+       log_journal("account_shadow(%llx, %llx, %x) %llx->%llx %x->%x\n",
+                       shadow->ino, shadow->bix, shadow->gc_level,
+                       shadow->old_ofs, shadow->new_ofs,
+                       shadow->old_len, shadow->new_len);
+       mempool_free(shadow, super->s_shadow_pool);
+}
+
+static void account_shadows(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *inode = super->s_master_inode;
+       struct logfs_inode *li = logfs_inode(inode);
+       struct shadow_tree *tree = &super->s_shadow_tree;
+
+       btree_grim_visitor64(&tree->new, (unsigned long)sb, account_shadow);
+       btree_grim_visitor64(&tree->old, (unsigned long)sb, account_shadow);
+
+       if (li->li_block) {
+               /*
+                * We never actually use the structure, when attached to the
+                * master inode.  But it is easier to always free it here than
+                * to have checks in several places elsewhere when allocating
+                * it.
+                */
+               li->li_block->ops->free_block(sb, li->li_block);
+       }
+       BUG_ON((s64)li->li_used_bytes < 0);
+}
+
+static void *__logfs_write_anchor(struct super_block *sb, void *_da,
+               u16 *type, size_t *len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_je_anchor *da = _da;
+       struct inode *inode = super->s_master_inode;
+       struct logfs_inode *li = logfs_inode(inode);
+       int i;
+
+       da->da_height   = li->li_height;
+       da->da_last_ino = cpu_to_be64(super->s_last_ino);
+       da->da_size     = cpu_to_be64(i_size_read(inode));
+       da->da_used_bytes = cpu_to_be64(li->li_used_bytes);
+       for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
+               da->da_data[i] = cpu_to_be64(li->li_data[i]);
+       *type = JE_ANCHOR;
+       *len = sizeof(*da);
+       return da;
+}
+
+static void *logfs_write_dynsb(struct super_block *sb, void *_dynsb,
+               u16 *type, size_t *len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_je_dynsb *dynsb = _dynsb;
+
+       dynsb->ds_gec           = cpu_to_be64(super->s_gec);
+       dynsb->ds_sweeper       = cpu_to_be64(super->s_sweeper);
+       dynsb->ds_victim_ino    = cpu_to_be64(super->s_victim_ino);
+       dynsb->ds_rename_dir    = cpu_to_be64(super->s_rename_dir);
+       dynsb->ds_rename_pos    = cpu_to_be64(super->s_rename_pos);
+       dynsb->ds_used_bytes    = cpu_to_be64(super->s_used_bytes);
+       dynsb->ds_generation    = cpu_to_be32(super->s_generation);
+       *type = JE_DYNSB;
+       *len = sizeof(*dynsb);
+       return dynsb;
+}
+
+static void write_wbuf(struct super_block *sb, struct logfs_area *area,
+               void *wbuf)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       u64 ofs;
+       pgoff_t index;
+       int page_ofs;
+       struct page *page;
+
+       ofs = dev_ofs(sb, area->a_segno,
+                       area->a_used_bytes & ~(super->s_writesize - 1));
+       index = ofs >> PAGE_SHIFT;
+       page_ofs = ofs & (PAGE_SIZE - 1);
+
+       page = find_lock_page(mapping, index);
+       BUG_ON(!page);
+       memcpy(wbuf, page_address(page) + page_ofs, super->s_writesize);
+       unlock_page(page);
+}
+
+static void *logfs_write_area(struct super_block *sb, void *_a,
+               u16 *type, size_t *len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area = super->s_area[super->s_sum_index];
+       struct logfs_je_area *a = _a;
+
+       a->vim = VIM_DEFAULT;
+       a->gc_level = super->s_sum_index;
+       a->used_bytes = cpu_to_be32(area->a_used_bytes);
+       a->segno = cpu_to_be32(area->a_segno);
+       if (super->s_writesize > 1)
+               write_wbuf(sb, area, a + 1);
+
+       *type = JE_AREA;
+       *len = sizeof(*a) + super->s_writesize;
+       return a;
+}
+
+static void *logfs_write_commit(struct super_block *sb, void *h,
+               u16 *type, size_t *len)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       *type = JE_COMMIT;
+       *len = super->s_no_je * sizeof(__be64);
+       return super->s_je_array;
+}
+
+static size_t __logfs_write_je(struct super_block *sb, void *buf, u16 type,
+               size_t len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       void *header = super->s_compressed_je;
+       void *data = header + sizeof(struct logfs_journal_header);
+       ssize_t compr_len, pad_len;
+       u8 compr = COMPR_ZLIB;
+
+       if (len == 0)
+               return logfs_write_header(super, header, 0, type);
+
+       compr_len = logfs_compress(buf, data, len, sb->s_blocksize);
+       if (compr_len < 0 || type == JE_ANCHOR) {
+               BUG_ON(len > sb->s_blocksize);
+               memcpy(data, buf, len);
+               compr_len = len;
+               compr = COMPR_NONE;
+       }
+
+       pad_len = ALIGN(compr_len, 16);
+       memset(data + compr_len, 0, pad_len - compr_len);
+
+       return __logfs_write_header(super, header, compr_len, len, type, compr);
+}
+
+static s64 logfs_get_free_bytes(struct logfs_area *area, size_t *bytes,
+               int must_pad)
+{
+       u32 writesize = logfs_super(area->a_sb)->s_writesize;
+       s32 ofs;
+       int ret;
+
+       ret = logfs_open_area(area, *bytes);
+       if (ret)
+               return -EAGAIN;
+
+       ofs = area->a_used_bytes;
+       area->a_used_bytes += *bytes;
+
+       if (must_pad) {
+               area->a_used_bytes = ALIGN(area->a_used_bytes, writesize);
+               *bytes = area->a_used_bytes - ofs;
+       }
+
+       return dev_ofs(area->a_sb, area->a_segno, ofs);
+}
+
+static int logfs_write_je_buf(struct super_block *sb, void *buf, u16 type,
+               size_t buf_len)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area = super->s_journal_area;
+       struct logfs_journal_header *jh = super->s_compressed_je;
+       size_t len;
+       int must_pad = 0;
+       s64 ofs;
+
+       len = __logfs_write_je(sb, buf, type, buf_len);
+       if (jh->h_type == cpu_to_be16(JE_COMMIT))
+               must_pad = 1;
+
+       ofs = logfs_get_free_bytes(area, &len, must_pad);
+       if (ofs < 0)
+               return ofs;
+       logfs_buf_write(area, ofs, super->s_compressed_je, len);
+       super->s_je_array[super->s_no_je++] = cpu_to_be64(ofs);
+       return 0;
+}
+
+static int logfs_write_je(struct super_block *sb,
+               void* (*write)(struct super_block *sb, void *scratch,
+                       u16 *type, size_t *len))
+{
+       void *buf;
+       size_t len;
+       u16 type;
+
+       buf = write(sb, logfs_super(sb)->s_je, &type, &len);
+       return logfs_write_je_buf(sb, buf, type, len);
+}
+
+int write_alias_journal(struct super_block *sb, u64 ino, u64 bix,
+               level_t level, int child_no, __be64 val)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_obj_alias *oa = super->s_je;
+       int err = 0, fill = super->s_je_fill;
+
+       log_aliases("logfs_write_obj_aliases #%x(%llx, %llx, %x, %x) %llx\n",
+                       fill, ino, bix, level, child_no, be64_to_cpu(val));
+       oa[fill].ino = cpu_to_be64(ino);
+       oa[fill].bix = cpu_to_be64(bix);
+       oa[fill].val = val;
+       oa[fill].level = (__force u8)level;
+       oa[fill].child_no = cpu_to_be16(child_no);
+       fill++;
+       if (fill >= sb->s_blocksize / sizeof(*oa)) {
+               err = logfs_write_je_buf(sb, oa, JE_OBJ_ALIAS, sb->s_blocksize);
+               fill = 0;
+       }
+
+       super->s_je_fill = fill;
+       return err;
+}
+
+static int logfs_write_obj_aliases(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int err;
+
+       log_journal("logfs_write_obj_aliases: %d aliases to write\n",
+                       super->s_no_object_aliases);
+       super->s_je_fill = 0;
+       err = logfs_write_obj_aliases_pagecache(sb);
+       if (err)
+               return err;
+
+       if (super->s_je_fill)
+               err = logfs_write_je_buf(sb, super->s_je, JE_OBJ_ALIAS,
+                               super->s_je_fill
+                               * sizeof(struct logfs_obj_alias));
+       return err;
+}
+
+/*
+ * Write all journal entries.  The goto logic ensures that all journal entries
+ * are written whenever a new segment is used.  It is ugly and potentially a
+ * bit wasteful, but robustness is more important.  With this we can *always*
+ * erase all journal segments except the one containing the most recent commit.
+ */
+void logfs_write_anchor(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area = super->s_journal_area;
+       int i, err;
+
+       if (!(super->s_flags & LOGFS_SB_FLAG_DIRTY))
+               return;
+       super->s_flags &= ~LOGFS_SB_FLAG_DIRTY;
+
+       BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN);
+       mutex_lock(&super->s_journal_mutex);
+
+       /* Do this first or suffer corruption */
+       logfs_sync_segments(sb);
+       account_shadows(sb);
+
+again:
+       super->s_no_je = 0;
+       for_each_area(i) {
+               if (!super->s_area[i]->a_is_open)
+                       continue;
+               super->s_sum_index = i;
+               err = logfs_write_je(sb, logfs_write_area);
+               if (err)
+                       goto again;
+       }
+       err = logfs_write_obj_aliases(sb);
+       if (err)
+               goto again;
+       err = logfs_write_je(sb, logfs_write_erasecount);
+       if (err)
+               goto again;
+       err = logfs_write_je(sb, __logfs_write_anchor);
+       if (err)
+               goto again;
+       err = logfs_write_je(sb, logfs_write_dynsb);
+       if (err)
+               goto again;
+       /*
+        * Order is imperative.  First we sync all writes, including the
+        * non-committed journal writes.  Then we write the final commit and
+        * sync the current journal segment.
+        * There is a theoretical bug here.  Syncing the journal segment will
+        * write a number of journal entries and the final commit.  All these
+        * are written in a single operation.  If the device layer writes the
+        * data back-to-front, the commit will precede the other journal
+        * entries, leaving a race window.
+        * Two fixes are possible.  Preferred is to fix the device layer to
+        * ensure writes happen front-to-back.  Alternatively we can insert
+        * another logfs_sync_area() super->s_devops->sync() combo before
+        * writing the commit.
+        */
+       /*
+        * On another subject, super->s_devops->sync is usually not necessary.
+        * Unless called from sys_sync or friends, a barrier would suffice.
+        */
+       super->s_devops->sync(sb);
+       err = logfs_write_je(sb, logfs_write_commit);
+       if (err)
+               goto again;
+       log_journal("Write commit to %llx\n",
+                       be64_to_cpu(super->s_je_array[super->s_no_je - 1]));
+       logfs_sync_area(area);
+       BUG_ON(area->a_used_bytes != area->a_written_bytes);
+       super->s_devops->sync(sb);
+
+       mutex_unlock(&super->s_journal_mutex);
+       return;
+}
+
+void do_logfs_journal_wl_pass(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_area *area = super->s_journal_area;
+       u32 segno, ec;
+       int i, err;
+
+       log_journal("Journal requires wear-leveling.\n");
+       /* Drop old segments */
+       journal_for_each(i)
+               if (super->s_journal_seg[i]) {
+                       logfs_set_segment_unreserved(sb,
+                                       super->s_journal_seg[i],
+                                       super->s_journal_ec[i]);
+                       super->s_journal_seg[i] = 0;
+                       super->s_journal_ec[i] = 0;
+               }
+       /* Get new segments */
+       for (i = 0; i < super->s_no_journal_segs; i++) {
+               segno = get_best_cand(sb, &super->s_reserve_list, &ec);
+               super->s_journal_seg[i] = segno;
+               super->s_journal_ec[i] = ec;
+               logfs_set_segment_reserved(sb, segno);
+       }
+       /* Manually move journal_area */
+       area->a_segno = super->s_journal_seg[0];
+       area->a_is_open = 0;
+       area->a_used_bytes = 0;
+       /* Write journal */
+       logfs_write_anchor(sb);
+       /* Write superblocks */
+       err = logfs_write_sb(sb);
+       BUG_ON(err);
+}
+
+static const struct logfs_area_ops journal_area_ops = {
+       .get_free_segment       = journal_get_free_segment,
+       .get_erase_count        = journal_get_erase_count,
+       .erase_segment          = journal_erase_segment,
+};
+
+int logfs_init_journal(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       size_t bufsize = max_t(size_t, sb->s_blocksize, super->s_writesize)
+               + MAX_JOURNAL_HEADER;
+       int ret = -ENOMEM;
+
+       mutex_init(&super->s_journal_mutex);
+       btree_init_mempool32(&super->s_reserved_segments, super->s_btree_pool);
+
+       super->s_je = kzalloc(bufsize, GFP_KERNEL);
+       if (!super->s_je)
+               return ret;
+
+       super->s_compressed_je = kzalloc(bufsize, GFP_KERNEL);
+       if (!super->s_compressed_je)
+               return ret;
+
+       super->s_master_inode = logfs_new_meta_inode(sb, LOGFS_INO_MASTER);
+       if (IS_ERR(super->s_master_inode))
+               return PTR_ERR(super->s_master_inode);
+
+       ret = logfs_read_journal(sb);
+       if (ret)
+               return -EIO;
+
+       reserve_sb_and_journal(sb);
+       logfs_calc_free(sb);
+
+       super->s_journal_area->a_ops = &journal_area_ops;
+       return 0;
+}
+
+void logfs_cleanup_journal(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       btree_grim_visitor32(&super->s_reserved_segments, 0, NULL);
+       destroy_meta_inode(super->s_master_inode);
+       super->s_master_inode = NULL;
+
+       kfree(super->s_compressed_je);
+       kfree(super->s_je);
+}
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h
new file mode 100644 (file)
index 0000000..1297794
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * fs/logfs/logfs.h
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ *
+ * Private header for logfs.
+ */
+#ifndef FS_LOGFS_LOGFS_H
+#define FS_LOGFS_LOGFS_H
+
+#undef __CHECK_ENDIAN__
+#define __CHECK_ENDIAN__
+
+#include <linux/btree.h>
+#include <linux/crc32.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mempool.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include "logfs_abi.h"
+
+#define LOGFS_DEBUG_SUPER      (0x0001)
+#define LOGFS_DEBUG_SEGMENT    (0x0002)
+#define LOGFS_DEBUG_JOURNAL    (0x0004)
+#define LOGFS_DEBUG_DIR                (0x0008)
+#define LOGFS_DEBUG_FILE       (0x0010)
+#define LOGFS_DEBUG_INODE      (0x0020)
+#define LOGFS_DEBUG_READWRITE  (0x0040)
+#define LOGFS_DEBUG_GC         (0x0080)
+#define LOGFS_DEBUG_GC_NOISY   (0x0100)
+#define LOGFS_DEBUG_ALIASES    (0x0200)
+#define LOGFS_DEBUG_BLOCKMOVE  (0x0400)
+#define LOGFS_DEBUG_ALL                (0xffffffff)
+
+#define LOGFS_DEBUG            (0x01)
+/*
+ * To enable specific log messages, simply define LOGFS_DEBUG to match any
+ * or all of the above.
+ */
+#ifndef LOGFS_DEBUG
+#define LOGFS_DEBUG            (0)
+#endif
+
+#define log_cond(cond, fmt, arg...) do {       \
+       if (cond)                               \
+               printk(KERN_DEBUG fmt, ##arg);  \
+} while (0)
+
+#define log_super(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_SUPER, fmt, ##arg)
+#define log_segment(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_SEGMENT, fmt, ##arg)
+#define log_journal(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_JOURNAL, fmt, ##arg)
+#define log_dir(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_DIR, fmt, ##arg)
+#define log_file(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_FILE, fmt, ##arg)
+#define log_inode(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_INODE, fmt, ##arg)
+#define log_readwrite(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_READWRITE, fmt, ##arg)
+#define log_gc(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_GC, fmt, ##arg)
+#define log_gc_noisy(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_GC_NOISY, fmt, ##arg)
+#define log_aliases(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_ALIASES, fmt, ##arg)
+#define log_blockmove(fmt, arg...) \
+       log_cond(LOGFS_DEBUG & LOGFS_DEBUG_BLOCKMOVE, fmt, ##arg)
+
+#define PG_pre_locked          PG_owner_priv_1
+#define PagePreLocked(page)    test_bit(PG_pre_locked, &(page)->flags)
+#define SetPagePreLocked(page) set_bit(PG_pre_locked, &(page)->flags)
+#define ClearPagePreLocked(page) clear_bit(PG_pre_locked, &(page)->flags)
+
+/* FIXME: This should really be somewhere in the 64bit area. */
+#define LOGFS_LINK_MAX         (1<<30)
+
+/* Read-only filesystem */
+#define LOGFS_SB_FLAG_RO       0x0001
+#define LOGFS_SB_FLAG_DIRTY    0x0002
+#define LOGFS_SB_FLAG_OBJ_ALIAS        0x0004
+#define LOGFS_SB_FLAG_SHUTDOWN 0x0008
+
+/* Write Control Flags */
+#define WF_LOCK                        0x01 /* take write lock */
+#define WF_WRITE               0x02 /* write block */
+#define WF_DELETE              0x04 /* delete old block */
+
+typedef u8 __bitwise level_t;
+typedef u8 __bitwise gc_level_t;
+
+#define LEVEL(level) ((__force level_t)(level))
+#define GC_LEVEL(gc_level) ((__force gc_level_t)(gc_level))
+
+#define SUBLEVEL(level) ( (void)((level) == LEVEL(1)), \
+               (__force level_t)((__force u8)(level) - 1) )
+
+/**
+ * struct logfs_area - area management information
+ *
+ * @a_sb:                      the superblock this area belongs to
+ * @a_is_open:                 1 if the area is currently open, else 0
+ * @a_segno:                   segment number of area
+ * @a_written_bytes:           number of bytes already written back
+ * @a_used_bytes:              number of used bytes
+ * @a_ops:                     area operations (either journal or ostore)
+ * @a_erase_count:             erase count
+ * @a_level:                   GC level
+ */
+struct logfs_area { /* a segment open for writing */
+       struct super_block *a_sb;
+       int     a_is_open;
+       u32     a_segno;
+       u32     a_written_bytes;
+       u32     a_used_bytes;
+       const struct logfs_area_ops *a_ops;
+       u32     a_erase_count;
+       gc_level_t a_level;
+};
+
+/**
+ * struct logfs_area_ops - area operations
+ *
+ * @get_free_segment:          fill area->ofs with the offset of a free segment
+ * @get_erase_count:           fill area->erase_count (needs area->ofs)
+ * @erase_segment:             erase and setup segment
+ */
+struct logfs_area_ops {
+       void    (*get_free_segment)(struct logfs_area *area);
+       void    (*get_erase_count)(struct logfs_area *area);
+       int     (*erase_segment)(struct logfs_area *area);
+};
+
+/**
+ * struct logfs_device_ops - device access operations
+ *
+ * @readpage:                  read one page (mm page)
+ * @writeseg:                  write one segment.  may be a partial segment
+ * @erase:                     erase one segment
+ * @read:                      read from the device
+ * @erase:                     erase part of the device
+ */
+struct logfs_device_ops {
+       struct page *(*find_first_sb)(struct super_block *sb, u64 *ofs);
+       struct page *(*find_last_sb)(struct super_block *sb, u64 *ofs);
+       int (*write_sb)(struct super_block *sb, struct page *page);
+       int (*readpage)(void *_sb, struct page *page);
+       void (*writeseg)(struct super_block *sb, u64 ofs, size_t len);
+       int (*erase)(struct super_block *sb, loff_t ofs, size_t len,
+                       int ensure_write);
+       void (*sync)(struct super_block *sb);
+       void (*put_device)(struct super_block *sb);
+};
+
+/**
+ * struct candidate_list - list of similar candidates
+ */
+struct candidate_list {
+       struct rb_root rb_tree;
+       int count;
+       int maxcount;
+       int sort_by_ec;
+};
+
+/**
+ * struct gc_candidate - "candidate" segment to be garbage collected next
+ *
+ * @list:                      list (either free of low)
+ * @segno:                     segment number
+ * @valid:                     number of valid bytes
+ * @erase_count:               erase count of segment
+ * @dist:                      distance from tree root
+ *
+ * Candidates can be on two lists.  The free list contains electees rather
+ * than candidates - segments that no longer contain any valid data.  The
+ * low list contains candidates to be picked for GC.  It should be kept
+ * short.  It is not required to always pick a perfect candidate.  In the
+ * worst case GC will have to move more data than absolutely necessary.
+ */
+struct gc_candidate {
+       struct rb_node rb_node;
+       struct candidate_list *list;
+       u32     segno;
+       u32     valid;
+       u32     erase_count;
+       u8      dist;
+};
+
+/**
+ * struct logfs_journal_entry - temporary structure used during journal scan
+ *
+ * @used:
+ * @version:                   normalized version
+ * @len:                       length
+ * @offset:                    offset
+ */
+struct logfs_journal_entry {
+       int used;
+       s16 version;
+       u16 len;
+       u16 datalen;
+       u64 offset;
+};
+
+enum transaction_state {
+       CREATE_1 = 1,
+       CREATE_2,
+       UNLINK_1,
+       UNLINK_2,
+       CROSS_RENAME_1,
+       CROSS_RENAME_2,
+       TARGET_RENAME_1,
+       TARGET_RENAME_2,
+       TARGET_RENAME_3
+};
+
+/**
+ * struct logfs_transaction - essential fields to support atomic dirops
+ *
+ * @ino:                       target inode
+ * @dir:                       inode of directory containing dentry
+ * @pos:                       pos of dentry in directory
+ */
+struct logfs_transaction {
+       enum transaction_state state;
+       u64      ino;
+       u64      dir;
+       u64      pos;
+};
+
+/**
+ * struct logfs_shadow - old block in the shadow of a not-yet-committed new one
+ * @old_ofs:                   offset of old block on medium
+ * @new_ofs:                   offset of new block on medium
+ * @ino:                       inode number
+ * @bix:                       block index
+ * @old_len:                   size of old block, including header
+ * @new_len:                   size of new block, including header
+ * @level:                     block level
+ */
+struct logfs_shadow {
+       u64 old_ofs;
+       u64 new_ofs;
+       u64 ino;
+       u64 bix;
+       int old_len;
+       int new_len;
+       gc_level_t gc_level;
+};
+
+/**
+ * struct shadow_tree
+ * @new:                       shadows where old_ofs==0, indexed by new_ofs
+ * @old:                       shadows where old_ofs!=0, indexed by old_ofs
+ */
+struct shadow_tree {
+       struct btree_head64 new;
+       struct btree_head64 old;
+};
+
+struct object_alias_item {
+       struct list_head list;
+       __be64 val;
+       int child_no;
+};
+
+/**
+ * struct logfs_block - contains any block state
+ * @type:                      indirect block or inode
+ * @full:                      number of fully populated children
+ * @partial:                   number of partially populated children
+ *
+ * Most blocks are directly represented by page cache pages.  But when a block
+ * becomes dirty, is part of a transaction, contains aliases or is otherwise
+ * special, a struct logfs_block is allocated to track the additional state.
+ * Inodes are very similar to indirect blocks, so they can also get one of
+ * these structures added when appropriate.
+ */
+#define BLOCK_INDIRECT 1       /* Indirect block */
+#define BLOCK_INODE    2       /* Inode */
+struct logfs_block_ops;
+struct logfs_block {
+       struct list_head alias_list;
+       struct list_head item_list;
+       struct super_block *sb;
+       u64 ino;
+       u64 bix;
+       level_t level;
+       struct page *page;
+       struct inode *inode;
+       struct logfs_transaction *ta;
+       unsigned long alias_map[LOGFS_BLOCK_FACTOR / BITS_PER_LONG];
+       struct logfs_block_ops *ops;
+       int full;
+       int partial;
+       int reserved_bytes;
+};
+
+typedef int write_alias_t(struct super_block *sb, u64 ino, u64 bix,
+               level_t level, int child_no, __be64 val);
+struct logfs_block_ops {
+       void    (*write_block)(struct logfs_block *block);
+       gc_level_t      (*block_level)(struct logfs_block *block);
+       void    (*free_block)(struct super_block *sb, struct logfs_block*block);
+       int     (*write_alias)(struct super_block *sb,
+                       struct logfs_block *block,
+                       write_alias_t *write_one_alias);
+};
+
+struct logfs_super {
+       struct mtd_info *s_mtd;                 /* underlying device */
+       struct block_device *s_bdev;            /* underlying device */
+       const struct logfs_device_ops *s_devops;/* device access */
+       struct inode    *s_master_inode;        /* inode file */
+       struct inode    *s_segfile_inode;       /* segment file */
+       struct inode *s_mapping_inode;          /* device mapping */
+       atomic_t s_pending_writes;              /* outstanting bios */
+       long     s_flags;
+       mempool_t *s_btree_pool;                /* for btree nodes */
+       mempool_t *s_alias_pool;                /* aliases in segment.c */
+       u64      s_feature_incompat;
+       u64      s_feature_ro_compat;
+       u64      s_feature_compat;
+       u64      s_feature_flags;
+       u64      s_sb_ofs[2];
+       struct page *s_erase_page;              /* for dev_bdev.c */
+       /* alias.c fields */
+       struct btree_head32 s_segment_alias;    /* remapped segments */
+       int      s_no_object_aliases;
+       struct list_head s_object_alias;        /* remapped objects */
+       struct btree_head128 s_object_alias_tree; /* remapped objects */
+       struct mutex s_object_alias_mutex;
+       /* dir.c fields */
+       struct mutex s_dirop_mutex;             /* for creat/unlink/rename */
+       u64      s_victim_ino;                  /* used for atomic dir-ops */
+       u64      s_rename_dir;                  /* source directory ino */
+       u64      s_rename_pos;                  /* position of source dd */
+       /* gc.c fields */
+       long     s_segsize;                     /* size of a segment */
+       int      s_segshift;                    /* log2 of segment size */
+       long     s_segmask;                     /* 1 << s_segshift - 1 */
+       long     s_no_segs;                     /* segments on device */
+       long     s_no_journal_segs;             /* segments used for journal */
+       long     s_no_blocks;                   /* blocks per segment */
+       long     s_writesize;                   /* minimum write size */
+       int      s_writeshift;                  /* log2 of write size */
+       u64      s_size;                        /* filesystem size */
+       struct logfs_area *s_area[LOGFS_NO_AREAS];      /* open segment array */
+       u64      s_gec;                         /* global erase count */
+       u64      s_wl_gec_ostore;               /* time of last wl event */
+       u64      s_wl_gec_journal;              /* time of last wl event */
+       u64      s_sweeper;                     /* current sweeper pos */
+       u8       s_ifile_levels;                /* max level of ifile */
+       u8       s_iblock_levels;               /* max level of regular files */
+       u8       s_data_levels;                 /* # of segments to leaf block*/
+       u8       s_total_levels;                /* sum of above three */
+       struct btree_head32 s_cand_tree;        /* all candidates */
+       struct candidate_list s_free_list;      /* 100% free segments */
+       struct candidate_list s_reserve_list;   /* Bad segment reserve */
+       struct candidate_list s_low_list[LOGFS_NO_AREAS];/* good candidates */
+       struct candidate_list s_ec_list;        /* wear level candidates */
+       struct btree_head32 s_reserved_segments;/* sb, journal, bad, etc. */
+       /* inode.c fields */
+       u64      s_last_ino;                    /* highest ino used */
+       long     s_inos_till_wrap;
+       u32      s_generation;                  /* i_generation for new files */
+       struct list_head s_freeing_list;        /* inodes being freed */
+       /* journal.c fields */
+       struct mutex s_journal_mutex;
+       void    *s_je;                          /* journal entry to compress */
+       void    *s_compressed_je;               /* block to write to journal */
+       u32      s_journal_seg[LOGFS_JOURNAL_SEGS]; /* journal segments */
+       u32      s_journal_ec[LOGFS_JOURNAL_SEGS]; /* journal erasecounts */
+       u64      s_last_version;
+       struct logfs_area *s_journal_area;      /* open journal segment */
+       __be64  s_je_array[64];
+       int     s_no_je;
+
+       int      s_sum_index;                   /* for the 12 summaries */
+       struct shadow_tree s_shadow_tree;
+       int      s_je_fill;                     /* index of current je */
+       /* readwrite.c fields */
+       struct mutex s_write_mutex;
+       int      s_lock_count;
+       mempool_t *s_block_pool;                /* struct logfs_block pool */
+       mempool_t *s_shadow_pool;               /* struct logfs_shadow pool */
+       /*
+        * Space accounting:
+        * - s_used_bytes specifies space used to store valid data objects.
+        * - s_dirty_used_bytes is space used to store non-committed data
+        *   objects.  Those objects have already been written themselves,
+        *   but they don't become valid until all indirect blocks up to the
+        *   journal have been written as well.
+        * - s_dirty_free_bytes is space used to store the old copy of a
+        *   replaced object, as long as the replacement is non-committed.
+        *   In other words, it is the amount of space freed when all dirty
+        *   blocks are written back.
+        * - s_free_bytes is the amount of free space available for any
+        *   purpose.
+        * - s_root_reserve is the amount of free space available only to
+        *   the root user.  Non-privileged users can no longer write once
+        *   this watermark has been reached.
+        * - s_speed_reserve is space which remains unused to speed up
+        *   garbage collection performance.
+        * - s_dirty_pages is the space reserved for currently dirty pages.
+        *   It is a pessimistic estimate, so some/most will get freed on
+        *   page writeback.
+        *
+        * s_used_bytes + s_free_bytes + s_speed_reserve = total usable size
+        */
+       u64      s_free_bytes;
+       u64      s_used_bytes;
+       u64      s_dirty_free_bytes;
+       u64      s_dirty_used_bytes;
+       u64      s_root_reserve;
+       u64      s_speed_reserve;
+       u64      s_dirty_pages;
+       /* Bad block handling:
+        * - s_bad_seg_reserve is a number of segments usually kept
+        *   free.  When encountering bad blocks, the affected segment's data
+        *   is _temporarily_ moved to a reserved segment.
+        * - s_bad_segments is the number of known bad segments.
+        */
+       u32      s_bad_seg_reserve;
+       u32      s_bad_segments;
+};
+
+/**
+ * struct logfs_inode - in-memory inode
+ *
+ * @vfs_inode:                 struct inode
+ * @li_data:                   data pointers
+ * @li_used_bytes:             number of used bytes
+ * @li_freeing_list:           used to track inodes currently being freed
+ * @li_flags:                  inode flags
+ * @li_refcount:               number of internal (GC-induced) references
+ */
+struct logfs_inode {
+       struct inode vfs_inode;
+       u64     li_data[LOGFS_EMBEDDED_FIELDS];
+       u64     li_used_bytes;
+       struct list_head li_freeing_list;
+       struct logfs_block *li_block;
+       u32     li_flags;
+       u8      li_height;
+       int     li_refcount;
+};
+
+#define journal_for_each(__i) for (__i = 0; __i < LOGFS_JOURNAL_SEGS; __i++)
+#define for_each_area(__i) for (__i = 0; __i < LOGFS_NO_AREAS; __i++)
+#define for_each_area_down(__i) for (__i = LOGFS_NO_AREAS - 1; __i >= 0; __i--)
+
+/* compr.c */
+int logfs_compress(void *in, void *out, size_t inlen, size_t outlen);
+int logfs_uncompress(void *in, void *out, size_t inlen, size_t outlen);
+int __init logfs_compr_init(void);
+void logfs_compr_exit(void);
+
+/* dev_bdev.c */
+#ifdef CONFIG_BLOCK
+int logfs_get_sb_bdev(struct file_system_type *type, int flags,
+               const char *devname, struct vfsmount *mnt);
+#else
+static inline int logfs_get_sb_bdev(struct file_system_type *type, int flags,
+               const char *devname, struct vfsmount *mnt)
+{
+       return -ENODEV;
+}
+#endif
+
+/* dev_mtd.c */
+#ifdef CONFIG_MTD
+int logfs_get_sb_mtd(struct file_system_type *type, int flags,
+               int mtdnr, struct vfsmount *mnt);
+#else
+static inline int logfs_get_sb_mtd(struct file_system_type *type, int flags,
+               int mtdnr, struct vfsmount *mnt)
+{
+       return -ENODEV;
+}
+#endif
+
+/* dir.c */
+extern const struct inode_operations logfs_symlink_iops;
+extern const struct inode_operations logfs_dir_iops;
+extern const struct file_operations logfs_dir_fops;
+int logfs_replay_journal(struct super_block *sb);
+
+/* file.c */
+extern const struct inode_operations logfs_reg_iops;
+extern const struct file_operations logfs_reg_fops;
+extern const struct address_space_operations logfs_reg_aops;
+int logfs_readpage(struct file *file, struct page *page);
+int logfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+               unsigned long arg);
+int logfs_fsync(struct file *file, struct dentry *dentry, int datasync);
+
+/* gc.c */
+u32 get_best_cand(struct super_block *sb, struct candidate_list *list, u32 *ec);
+void logfs_gc_pass(struct super_block *sb);
+int logfs_check_areas(struct super_block *sb);
+int logfs_init_gc(struct super_block *sb);
+void logfs_cleanup_gc(struct super_block *sb);
+
+/* inode.c */
+extern const struct super_operations logfs_super_operations;
+struct inode *logfs_iget(struct super_block *sb, ino_t ino);
+struct inode *logfs_safe_iget(struct super_block *sb, ino_t ino, int *cookie);
+void logfs_safe_iput(struct inode *inode, int cookie);
+struct inode *logfs_new_inode(struct inode *dir, int mode);
+struct inode *logfs_new_meta_inode(struct super_block *sb, u64 ino);
+struct inode *logfs_read_meta_inode(struct super_block *sb, u64 ino);
+int logfs_init_inode_cache(void);
+void logfs_destroy_inode_cache(void);
+void destroy_meta_inode(struct inode *inode);
+void logfs_set_blocks(struct inode *inode, u64 no);
+/* these logically belong into inode.c but actually reside in readwrite.c */
+int logfs_read_inode(struct inode *inode);
+int __logfs_write_inode(struct inode *inode, long flags);
+void logfs_delete_inode(struct inode *inode);
+void logfs_clear_inode(struct inode *inode);
+
+/* journal.c */
+void logfs_write_anchor(struct super_block *sb);
+int logfs_init_journal(struct super_block *sb);
+void logfs_cleanup_journal(struct super_block *sb);
+int write_alias_journal(struct super_block *sb, u64 ino, u64 bix,
+               level_t level, int child_no, __be64 val);
+void do_logfs_journal_wl_pass(struct super_block *sb);
+
+/* readwrite.c */
+pgoff_t logfs_pack_index(u64 bix, level_t level);
+void logfs_unpack_index(pgoff_t index, u64 *bix, level_t *level);
+int logfs_inode_write(struct inode *inode, const void *buf, size_t count,
+               loff_t bix, long flags, struct shadow_tree *shadow_tree);
+int logfs_readpage_nolock(struct page *page);
+int logfs_write_buf(struct inode *inode, struct page *page, long flags);
+int logfs_delete(struct inode *inode, pgoff_t index,
+               struct shadow_tree *shadow_tree);
+int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
+               gc_level_t gc_level, long flags);
+int logfs_is_valid_block(struct super_block *sb, u64 ofs, u64 ino, u64 bix,
+               gc_level_t gc_level);
+int logfs_truncate(struct inode *inode, u64 size);
+u64 logfs_seek_hole(struct inode *inode, u64 bix);
+u64 logfs_seek_data(struct inode *inode, u64 bix);
+int logfs_open_segfile(struct super_block *sb);
+int logfs_init_rw(struct super_block *sb);
+void logfs_cleanup_rw(struct super_block *sb);
+void logfs_add_transaction(struct inode *inode, struct logfs_transaction *ta);
+void logfs_del_transaction(struct inode *inode, struct logfs_transaction *ta);
+void logfs_write_block(struct logfs_block *block, long flags);
+int logfs_write_obj_aliases_pagecache(struct super_block *sb);
+void logfs_get_segment_entry(struct super_block *sb, u32 segno,
+               struct logfs_segment_entry *se);
+void logfs_set_segment_used(struct super_block *sb, u64 ofs, int increment);
+void logfs_set_segment_erased(struct super_block *sb, u32 segno, u32 ec,
+               gc_level_t gc_level);
+void logfs_set_segment_reserved(struct super_block *sb, u32 segno);
+void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec);
+struct logfs_block *__alloc_block(struct super_block *sb,
+               u64 ino, u64 bix, level_t level);
+void __free_block(struct super_block *sb, struct logfs_block *block);
+void btree_write_block(struct logfs_block *block);
+void initialize_block_counters(struct page *page, struct logfs_block *block,
+               __be64 *array, int page_is_empty);
+int logfs_exist_block(struct inode *inode, u64 bix);
+int get_page_reserve(struct inode *inode, struct page *page);
+extern struct logfs_block_ops indirect_block_ops;
+
+/* segment.c */
+int logfs_erase_segment(struct super_block *sb, u32 ofs, int ensure_erase);
+int wbuf_read(struct super_block *sb, u64 ofs, size_t len, void *buf);
+int logfs_segment_read(struct inode *inode, struct page *page, u64 ofs, u64 bix,
+               level_t level);
+int logfs_segment_write(struct inode *inode, struct page *page,
+               struct logfs_shadow *shadow);
+int logfs_segment_delete(struct inode *inode, struct logfs_shadow *shadow);
+int logfs_load_object_aliases(struct super_block *sb,
+               struct logfs_obj_alias *oa, int count);
+void move_page_to_btree(struct page *page);
+int logfs_init_mapping(struct super_block *sb);
+void logfs_sync_area(struct logfs_area *area);
+void logfs_sync_segments(struct super_block *sb);
+
+/* area handling */
+int logfs_init_areas(struct super_block *sb);
+void logfs_cleanup_areas(struct super_block *sb);
+int logfs_open_area(struct logfs_area *area, size_t bytes);
+void __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
+               int use_filler);
+
+static inline void logfs_buf_write(struct logfs_area *area, u64 ofs,
+               void *buf, size_t len)
+{
+       __logfs_buf_write(area, ofs, buf, len, 0);
+}
+
+static inline void logfs_buf_recover(struct logfs_area *area, u64 ofs,
+               void *buf, size_t len)
+{
+       __logfs_buf_write(area, ofs, buf, len, 1);
+}
+
+/* super.c */
+struct page *emergency_read_begin(struct address_space *mapping, pgoff_t index);
+void emergency_read_end(struct page *page);
+void logfs_crash_dump(struct super_block *sb);
+void *memchr_inv(const void *s, int c, size_t n);
+int logfs_statfs(struct dentry *dentry, struct kstatfs *stats);
+int logfs_get_sb_device(struct file_system_type *type, int flags,
+               struct mtd_info *mtd, struct block_device *bdev,
+               const struct logfs_device_ops *devops, struct vfsmount *mnt);
+int logfs_check_ds(struct logfs_disk_super *ds);
+int logfs_write_sb(struct super_block *sb);
+
+static inline struct logfs_super *logfs_super(struct super_block *sb)
+{
+       return sb->s_fs_info;
+}
+
+static inline struct logfs_inode *logfs_inode(struct inode *inode)
+{
+       return container_of(inode, struct logfs_inode, vfs_inode);
+}
+
+static inline void logfs_set_ro(struct super_block *sb)
+{
+       logfs_super(sb)->s_flags |= LOGFS_SB_FLAG_RO;
+}
+
+#define LOGFS_BUG(sb) do {                                     \
+       struct super_block *__sb = sb;                          \
+       logfs_crash_dump(__sb);                                 \
+       logfs_super(__sb)->s_flags |= LOGFS_SB_FLAG_RO;         \
+       BUG();                                                  \
+} while (0)
+
+#define LOGFS_BUG_ON(condition, sb) \
+       do { if (unlikely(condition)) LOGFS_BUG((sb)); } while (0)
+
+static inline __be32 logfs_crc32(void *data, size_t len, size_t skip)
+{
+       return cpu_to_be32(crc32(~0, data+skip, len-skip));
+}
+
+static inline u8 logfs_type(struct inode *inode)
+{
+       return (inode->i_mode >> 12) & 15;
+}
+
+static inline pgoff_t logfs_index(struct super_block *sb, u64 pos)
+{
+       return pos >> sb->s_blocksize_bits;
+}
+
+static inline u64 dev_ofs(struct super_block *sb, u32 segno, u32 ofs)
+{
+       return ((u64)segno << logfs_super(sb)->s_segshift) + ofs;
+}
+
+static inline u32 seg_no(struct super_block *sb, u64 ofs)
+{
+       return ofs >> logfs_super(sb)->s_segshift;
+}
+
+static inline u32 seg_ofs(struct super_block *sb, u64 ofs)
+{
+       return ofs & logfs_super(sb)->s_segmask;
+}
+
+static inline u64 seg_align(struct super_block *sb, u64 ofs)
+{
+       return ofs & ~logfs_super(sb)->s_segmask;
+}
+
+static inline struct logfs_block *logfs_block(struct page *page)
+{
+       return (void *)page->private;
+}
+
+static inline level_t shrink_level(gc_level_t __level)
+{
+       u8 level = (__force u8)__level;
+
+       if (level >= LOGFS_MAX_LEVELS)
+               level -= LOGFS_MAX_LEVELS;
+       return (__force level_t)level;
+}
+
+static inline gc_level_t expand_level(u64 ino, level_t __level)
+{
+       u8 level = (__force u8)__level;
+
+       if (ino == LOGFS_INO_MASTER) {
+               /* ifile has seperate areas */
+               level += LOGFS_MAX_LEVELS;
+       }
+       return (__force gc_level_t)level;
+}
+
+static inline int logfs_block_shift(struct super_block *sb, level_t level)
+{
+       level = shrink_level((__force gc_level_t)level);
+       return (__force int)level * (sb->s_blocksize_bits - 3);
+}
+
+static inline u64 logfs_block_mask(struct super_block *sb, level_t level)
+{
+       return ~0ull << logfs_block_shift(sb, level);
+}
+
+static inline struct logfs_area *get_area(struct super_block *sb,
+               gc_level_t gc_level)
+{
+       return logfs_super(sb)->s_area[(__force u8)gc_level];
+}
+
+#endif
diff --git a/fs/logfs/logfs_abi.h b/fs/logfs/logfs_abi.h
new file mode 100644 (file)
index 0000000..f674725
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * fs/logfs/logfs_abi.h
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ *
+ * Public header for logfs.
+ */
+#ifndef FS_LOGFS_LOGFS_ABI_H
+#define FS_LOGFS_LOGFS_ABI_H
+
+/* For out-of-kernel compiles */
+#ifndef BUILD_BUG_ON
+#define BUILD_BUG_ON(condition) /**/
+#endif
+
+#define SIZE_CHECK(type, size)                                 \
+static inline void check_##type(void)                          \
+{                                                              \
+       BUILD_BUG_ON(sizeof(struct type) != (size));            \
+}
+
+/*
+ * Throughout the logfs code, we're constantly dealing with blocks at
+ * various positions or offsets.  To remove confusion, we stricly
+ * distinguish between a "position" - the logical position within a
+ * file and an "offset" - the physical location within the device.
+ *
+ * Any usage of the term offset for a logical location or position for
+ * a physical one is a bug and should get fixed.
+ */
+
+/*
+ * Block are allocated in one of several segments depending on their
+ * level.  The following levels are used:
+ *  0  - regular data block
+ *  1  - i1 indirect blocks
+ *  2  - i2 indirect blocks
+ *  3  - i3 indirect blocks
+ *  4  - i4 indirect blocks
+ *  5  - i5 indirect blocks
+ *  6  - ifile data blocks
+ *  7  - ifile i1 indirect blocks
+ *  8  - ifile i2 indirect blocks
+ *  9  - ifile i3 indirect blocks
+ * 10  - ifile i4 indirect blocks
+ * 11  - ifile i5 indirect blocks
+ * Potential levels to be used in the future:
+ * 12  - gc recycled blocks, long-lived data
+ * 13  - replacement blocks, short-lived data
+ *
+ * Levels 1-11 are necessary for robust gc operations and help seperate
+ * short-lived metadata from longer-lived file data.  In the future,
+ * file data should get seperated into several segments based on simple
+ * heuristics.  Old data recycled during gc operation is expected to be
+ * long-lived.  New data is of uncertain life expectancy.  New data
+ * used to replace older blocks in existing files is expected to be
+ * short-lived.
+ */
+
+
+/* Magic numbers.  64bit for superblock, 32bit for statfs f_type */
+#define LOGFS_MAGIC            0x7a3a8e5cb9d5bf67ull
+#define LOGFS_MAGIC_U32                0xc97e8168u
+
+/*
+ * Various blocksize related macros.  Blocksize is currently fixed at 4KiB.
+ * Sooner or later that should become configurable and the macros replaced
+ * by something superblock-dependent.  Pointers in indirect blocks are and
+ * will remain 64bit.
+ *
+ * LOGFS_BLOCKSIZE     - self-explaining
+ * LOGFS_BLOCK_FACTOR  - number of pointers per indirect block
+ * LOGFS_BLOCK_BITS    - log2 of LOGFS_BLOCK_FACTOR, used for shifts
+ */
+#define LOGFS_BLOCKSIZE                (4096ull)
+#define LOGFS_BLOCK_FACTOR     (LOGFS_BLOCKSIZE / sizeof(u64))
+#define LOGFS_BLOCK_BITS       (9)
+
+/*
+ * Number of blocks at various levels of indirection.  There are 16 direct
+ * block pointers plus a single indirect pointer.
+ */
+#define I0_BLOCKS              (16)
+#define I1_BLOCKS              LOGFS_BLOCK_FACTOR
+#define I2_BLOCKS              (LOGFS_BLOCK_FACTOR * I1_BLOCKS)
+#define I3_BLOCKS              (LOGFS_BLOCK_FACTOR * I2_BLOCKS)
+#define I4_BLOCKS              (LOGFS_BLOCK_FACTOR * I3_BLOCKS)
+#define I5_BLOCKS              (LOGFS_BLOCK_FACTOR * I4_BLOCKS)
+
+#define INDIRECT_INDEX         I0_BLOCKS
+#define LOGFS_EMBEDDED_FIELDS  (I0_BLOCKS + 1)
+
+/*
+ * Sizes at which files require another level of indirection.  Files smaller
+ * than LOGFS_EMBEDDED_SIZE can be completely stored in the inode itself,
+ * similar like ext2 fast symlinks.
+ *
+ * Data at a position smaller than LOGFS_I0_SIZE is accessed through the
+ * direct pointers, else through the 1x indirect pointer and so forth.
+ */
+#define LOGFS_EMBEDDED_SIZE    (LOGFS_EMBEDDED_FIELDS * sizeof(u64))
+#define LOGFS_I0_SIZE          (I0_BLOCKS * LOGFS_BLOCKSIZE)
+#define LOGFS_I1_SIZE          (I1_BLOCKS * LOGFS_BLOCKSIZE)
+#define LOGFS_I2_SIZE          (I2_BLOCKS * LOGFS_BLOCKSIZE)
+#define LOGFS_I3_SIZE          (I3_BLOCKS * LOGFS_BLOCKSIZE)
+#define LOGFS_I4_SIZE          (I4_BLOCKS * LOGFS_BLOCKSIZE)
+#define LOGFS_I5_SIZE          (I5_BLOCKS * LOGFS_BLOCKSIZE)
+
+/*
+ * Each indirect block pointer must have this flag set, if all block pointers
+ * behind it are set, i.e. there is no hole hidden in the shadow of this
+ * indirect block pointer.
+ */
+#define LOGFS_FULLY_POPULATED (1ULL << 63)
+#define pure_ofs(ofs) (ofs & ~LOGFS_FULLY_POPULATED)
+
+/*
+ * LogFS needs to seperate data into levels.  Each level is defined as the
+ * maximal possible distance from the master inode (inode of the inode file).
+ * Data blocks reside on level 0, 1x indirect block on level 1, etc.
+ * Inodes reside on level 6, indirect blocks for the inode file on levels 7-11.
+ * This effort is necessary to guarantee garbage collection to always make
+ * progress.
+ *
+ * LOGFS_MAX_INDIRECT is the maximal indirection through indirect blocks,
+ * LOGFS_MAX_LEVELS is one more for the actual data level of a file.  It is
+ * the maximal number of levels for one file.
+ * LOGFS_NO_AREAS is twice that, as the inode file and regular files are
+ * effectively stacked on top of each other.
+ */
+#define LOGFS_MAX_INDIRECT     (5)
+#define LOGFS_MAX_LEVELS       (LOGFS_MAX_INDIRECT + 1)
+#define LOGFS_NO_AREAS         (2 * LOGFS_MAX_LEVELS)
+
+/* Maximum size of filenames */
+#define LOGFS_MAX_NAMELEN      (255)
+
+/* Number of segments in the primary journal. */
+#define LOGFS_JOURNAL_SEGS     (16)
+
+/* Maximum number of free/erased/etc. segments in journal entries */
+#define MAX_CACHED_SEGS                (64)
+
+
+/*
+ * LOGFS_OBJECT_HEADERSIZE is the size of a single header in the object store,
+ * LOGFS_MAX_OBJECTSIZE the size of the largest possible object, including
+ * its header,
+ * LOGFS_SEGMENT_RESERVE is the amount of space reserved for each segment for
+ * its segment header and the padded space at the end when no further objects
+ * fit.
+ */
+#define LOGFS_OBJECT_HEADERSIZE        (0x1c)
+#define LOGFS_SEGMENT_HEADERSIZE (0x18)
+#define LOGFS_MAX_OBJECTSIZE   (LOGFS_OBJECT_HEADERSIZE + LOGFS_BLOCKSIZE)
+#define LOGFS_SEGMENT_RESERVE  \
+       (LOGFS_SEGMENT_HEADERSIZE + LOGFS_MAX_OBJECTSIZE - 1)
+
+/*
+ * Segment types:
+ * SEG_SUPER   - Data or indirect block
+ * SEG_JOURNAL - Inode
+ * SEG_OSTORE  - Dentry
+ */
+enum {
+       SEG_SUPER       = 0x01,
+       SEG_JOURNAL     = 0x02,
+       SEG_OSTORE      = 0x03,
+};
+
+/**
+ * struct logfs_segment_header - per-segment header in the ostore
+ *
+ * @crc:                       crc32 of header (there is no data)
+ * @pad:                       unused, must be 0
+ * @type:                      segment type, see above
+ * @level:                     GC level for all objects in this segment
+ * @segno:                     segment number
+ * @ec:                                erase count for this segment
+ * @gec:                       global erase count at time of writing
+ */
+struct logfs_segment_header {
+       __be32  crc;
+       __be16  pad;
+       __u8    type;
+       __u8    level;
+       __be32  segno;
+       __be32  ec;
+       __be64  gec;
+};
+
+SIZE_CHECK(logfs_segment_header, LOGFS_SEGMENT_HEADERSIZE);
+
+#define LOGFS_FEATURES_INCOMPAT                (0ull)
+#define LOGFS_FEATURES_RO_COMPAT       (0ull)
+#define LOGFS_FEATURES_COMPAT          (0ull)
+
+/**
+ * struct logfs_disk_super - on-medium superblock
+ *
+ * @ds_magic:                  magic number, must equal LOGFS_MAGIC
+ * @ds_crc:                    crc32 of structure starting with the next field
+ * @ds_ifile_levels:           maximum number of levels for ifile
+ * @ds_iblock_levels:          maximum number of levels for regular files
+ * @ds_data_levels:            number of seperate levels for data
+ * @pad0:                      reserved, must be 0
+ * @ds_feature_incompat:       incompatible filesystem features
+ * @ds_feature_ro_compat:      read-only compatible filesystem features
+ * @ds_feature_compat:         compatible filesystem features
+ * @ds_flags:                  flags
+ * @ds_segment_shift:          log2 of segment size
+ * @ds_block_shift:            log2 of block size
+ * @ds_write_shift:            log2 of write size
+ * @pad1:                      reserved, must be 0
+ * @ds_journal_seg:            segments used by primary journal
+ * @ds_root_reserve:           bytes reserved for the superuser
+ * @ds_speed_reserve:          bytes reserved to speed up GC
+ * @ds_bad_seg_reserve:                number of segments reserved to handle bad blocks
+ * @pad2:                      reserved, must be 0
+ * @pad3:                      reserved, must be 0
+ *
+ * Contains only read-only fields.  Read-write fields like the amount of used
+ * space is tracked in the dynamic superblock, which is stored in the journal.
+ */
+struct logfs_disk_super {
+       struct logfs_segment_header ds_sh;
+       __be64  ds_magic;
+
+       __be32  ds_crc;
+       __u8    ds_ifile_levels;
+       __u8    ds_iblock_levels;
+       __u8    ds_data_levels;
+       __u8    ds_segment_shift;
+       __u8    ds_block_shift;
+       __u8    ds_write_shift;
+       __u8    pad0[6];
+
+       __be64  ds_filesystem_size;
+       __be32  ds_segment_size;
+       __be32  ds_bad_seg_reserve;
+
+       __be64  ds_feature_incompat;
+       __be64  ds_feature_ro_compat;
+
+       __be64  ds_feature_compat;
+       __be64  ds_feature_flags;
+
+       __be64  ds_root_reserve;
+       __be64  ds_speed_reserve;
+
+       __be32  ds_journal_seg[LOGFS_JOURNAL_SEGS];
+
+       __be64  ds_super_ofs[2];
+       __be64  pad3[8];
+};
+
+SIZE_CHECK(logfs_disk_super, 256);
+
+/*
+ * Object types:
+ * OBJ_BLOCK   - Data or indirect block
+ * OBJ_INODE   - Inode
+ * OBJ_DENTRY  - Dentry
+ */
+enum {
+       OBJ_BLOCK       = 0x04,
+       OBJ_INODE       = 0x05,
+       OBJ_DENTRY      = 0x06,
+};
+
+/**
+ * struct logfs_object_header - per-object header in the ostore
+ *
+ * @crc:                       crc32 of header, excluding data_crc
+ * @len:                       length of data
+ * @type:                      object type, see above
+ * @compr:                     compression type
+ * @ino:                       inode number
+ * @bix:                       block index
+ * @data_crc:                  crc32 of payload
+ */
+struct logfs_object_header {
+       __be32  crc;
+       __be16  len;
+       __u8    type;
+       __u8    compr;
+       __be64  ino;
+       __be64  bix;
+       __be32  data_crc;
+} __attribute__((packed));
+
+SIZE_CHECK(logfs_object_header, LOGFS_OBJECT_HEADERSIZE);
+
+/*
+ * Reserved inode numbers:
+ * LOGFS_INO_MASTER    - master inode (for inode file)
+ * LOGFS_INO_ROOT      - root directory
+ * LOGFS_INO_SEGFILE   - per-segment used bytes and erase count
+ */
+enum {
+       LOGFS_INO_MAPPING       = 0x00,
+       LOGFS_INO_MASTER        = 0x01,
+       LOGFS_INO_ROOT          = 0x02,
+       LOGFS_INO_SEGFILE       = 0x03,
+       LOGFS_RESERVED_INOS     = 0x10,
+};
+
+/*
+ * Inode flags.  High bits should never be written to the medium.  They are
+ * reserved for in-memory usage.
+ * Low bits should either remain in sync with the corresponding FS_*_FL or
+ * reuse slots that obviously don't make sense for logfs.
+ *
+ * LOGFS_IF_DIRTY      Inode must be written back
+ * LOGFS_IF_ZOMBIE     Inode has been deleted
+ * LOGFS_IF_STILLBORN  -ENOSPC happened when creating inode
+ */
+#define LOGFS_IF_COMPRESSED    0x00000004 /* == FS_COMPR_FL */
+#define LOGFS_IF_DIRTY         0x20000000
+#define LOGFS_IF_ZOMBIE                0x40000000
+#define LOGFS_IF_STILLBORN     0x80000000
+
+/* Flags available to chattr */
+#define LOGFS_FL_USER_VISIBLE  (LOGFS_IF_COMPRESSED)
+#define LOGFS_FL_USER_MODIFIABLE (LOGFS_IF_COMPRESSED)
+/* Flags inherited from parent directory on file/directory creation */
+#define LOGFS_FL_INHERITED     (LOGFS_IF_COMPRESSED)
+
+/**
+ * struct logfs_disk_inode - on-medium inode
+ *
+ * @di_mode:                   file mode
+ * @di_pad:                    reserved, must be 0
+ * @di_flags:                  inode flags, see above
+ * @di_uid:                    user id
+ * @di_gid:                    group id
+ * @di_ctime:                  change time
+ * @di_mtime:                  modify time
+ * @di_refcount:               reference count (aka nlink or link count)
+ * @di_generation:             inode generation, for nfs
+ * @di_used_bytes:             number of bytes used
+ * @di_size:                   file size
+ * @di_data:                   data pointers
+ */
+struct logfs_disk_inode {
+       __be16  di_mode;
+       __u8    di_height;
+       __u8    di_pad;
+       __be32  di_flags;
+       __be32  di_uid;
+       __be32  di_gid;
+
+       __be64  di_ctime;
+       __be64  di_mtime;
+
+       __be64  di_atime;
+       __be32  di_refcount;
+       __be32  di_generation;
+
+       __be64  di_used_bytes;
+       __be64  di_size;
+
+       __be64  di_data[LOGFS_EMBEDDED_FIELDS];
+};
+
+SIZE_CHECK(logfs_disk_inode, 200);
+
+#define INODE_POINTER_OFS \
+       (offsetof(struct logfs_disk_inode, di_data) / sizeof(__be64))
+#define INODE_USED_OFS \
+       (offsetof(struct logfs_disk_inode, di_used_bytes) / sizeof(__be64))
+#define INODE_SIZE_OFS \
+       (offsetof(struct logfs_disk_inode, di_size) / sizeof(__be64))
+#define INODE_HEIGHT_OFS       (0)
+
+/**
+ * struct logfs_disk_dentry - on-medium dentry structure
+ *
+ * @ino:                       inode number
+ * @namelen:                   length of file name
+ * @type:                      file type, identical to bits 12..15 of mode
+ * @name:                      file name
+ */
+/* FIXME: add 6 bytes of padding to remove the __packed */
+struct logfs_disk_dentry {
+       __be64  ino;
+       __be16  namelen;
+       __u8    type;
+       __u8    name[LOGFS_MAX_NAMELEN];
+} __attribute__((packed));
+
+SIZE_CHECK(logfs_disk_dentry, 266);
+
+#define RESERVED               0xffffffff
+#define BADSEG                 0xffffffff
+/**
+ * struct logfs_segment_entry - segment file entry
+ *
+ * @ec_level:                  erase count and level
+ * @valid:                     number of valid bytes
+ *
+ * Segment file contains one entry for every segment.  ec_level contains the
+ * erasecount in the upper 28 bits and the level in the lower 4 bits.  An
+ * ec_level of BADSEG (-1) identifies bad segments.  valid contains the number
+ * of valid bytes or RESERVED (-1 again) if the segment is used for either the
+ * superblock or the journal, or when the segment is bad.
+ */
+struct logfs_segment_entry {
+       __be32  ec_level;
+       __be32  valid;
+};
+
+SIZE_CHECK(logfs_segment_entry, 8);
+
+/**
+ * struct logfs_journal_header - header for journal entries (JEs)
+ *
+ * @h_crc:                     crc32 of journal entry
+ * @h_len:                     length of compressed journal entry,
+ *                             not including header
+ * @h_datalen:                 length of uncompressed data
+ * @h_type:                    JE type
+ * @h_compr:                   compression type
+ * @h_pad:                     reserved
+ */
+struct logfs_journal_header {
+       __be32  h_crc;
+       __be16  h_len;
+       __be16  h_datalen;
+       __be16  h_type;
+       __u8    h_compr;
+       __u8    h_pad[5];
+};
+
+SIZE_CHECK(logfs_journal_header, 16);
+
+/*
+ * Life expectency of data.
+ * VIM_DEFAULT         - default vim
+ * VIM_SEGFILE         - for segment file only - very short-living
+ * VIM_GC              - GC'd data - likely long-living
+ */
+enum logfs_vim {
+       VIM_DEFAULT     = 0,
+       VIM_SEGFILE     = 1,
+};
+
+/**
+ * struct logfs_je_area - wbuf header
+ *
+ * @segno:                     segment number of area
+ * @used_bytes:                        number of bytes already used
+ * @gc_level:                  GC level
+ * @vim:                       life expectancy of data
+ *
+ * "Areas" are segments currently being used for writing.  There is at least
+ * one area per GC level.  Several may be used to seperate long-living from
+ * short-living data.  If an area with unknown vim is encountered, it can
+ * simply be closed.
+ * The write buffer immediately follow this header.
+ */
+struct logfs_je_area {
+       __be32  segno;
+       __be32  used_bytes;
+       __u8    gc_level;
+       __u8    vim;
+} __attribute__((packed));
+
+SIZE_CHECK(logfs_je_area, 10);
+
+#define MAX_JOURNAL_HEADER \
+       (sizeof(struct logfs_journal_header) + sizeof(struct logfs_je_area))
+
+/**
+ * struct logfs_je_dynsb - dynamic superblock
+ *
+ * @ds_gec:                    global erase count
+ * @ds_sweeper:                        current position of GC "sweeper"
+ * @ds_rename_dir:             source directory ino (see dir.c documentation)
+ * @ds_rename_pos:             position of source dd (see dir.c documentation)
+ * @ds_victim_ino:             victims of incomplete dir operation (see dir.c)
+ * @ds_victim_ino:             parent inode of victim (see dir.c)
+ * @ds_used_bytes:             number of used bytes
+ */
+struct logfs_je_dynsb {
+       __be64  ds_gec;
+       __be64  ds_sweeper;
+
+       __be64  ds_rename_dir;
+       __be64  ds_rename_pos;
+
+       __be64  ds_victim_ino;
+       __be64  ds_victim_parent; /* XXX */
+
+       __be64  ds_used_bytes;
+       __be32  ds_generation;
+       __be32  pad;
+};
+
+SIZE_CHECK(logfs_je_dynsb, 64);
+
+/**
+ * struct logfs_je_anchor - anchor of filesystem tree, aka master inode
+ *
+ * @da_size:                   size of inode file
+ * @da_last_ino:               last created inode
+ * @da_used_bytes:             number of bytes used
+ * @da_data:                   data pointers
+ */
+struct logfs_je_anchor {
+       __be64  da_size;
+       __be64  da_last_ino;
+
+       __be64  da_used_bytes;
+       u8      da_height;
+       u8      pad[7];
+
+       __be64  da_data[LOGFS_EMBEDDED_FIELDS];
+};
+
+SIZE_CHECK(logfs_je_anchor, 168);
+
+/**
+ * struct logfs_je_spillout - spillout entry (from 1st to 2nd journal)
+ *
+ * @so_segment:                        segments used for 2nd journal
+ *
+ * Length of the array is given by h_len field in the header.
+ */
+struct logfs_je_spillout {
+       __be64  so_segment[0];
+};
+
+SIZE_CHECK(logfs_je_spillout, 0);
+
+/**
+ * struct logfs_je_journal_ec - erase counts for all journal segments
+ *
+ * @ec:                                erase count
+ *
+ * Length of the array is given by h_len field in the header.
+ */
+struct logfs_je_journal_ec {
+       __be32  ec[0];
+};
+
+SIZE_CHECK(logfs_je_journal_ec, 0);
+
+/**
+ * struct logfs_je_free_segments - list of free segmetns with erase count
+ */
+struct logfs_je_free_segments {
+       __be32  segno;
+       __be32  ec;
+};
+
+SIZE_CHECK(logfs_je_free_segments, 8);
+
+/**
+ * struct logfs_seg_alias - list of segment aliases
+ */
+struct logfs_seg_alias {
+       __be32  old_segno;
+       __be32  new_segno;
+};
+
+SIZE_CHECK(logfs_seg_alias, 8);
+
+/**
+ * struct logfs_obj_alias - list of object aliases
+ */
+struct logfs_obj_alias {
+       __be64  ino;
+       __be64  bix;
+       __be64  val;
+       u8      level;
+       u8      pad[5];
+       __be16  child_no;
+};
+
+SIZE_CHECK(logfs_obj_alias, 32);
+
+/**
+ * Compression types.
+ *
+ * COMPR_NONE  - uncompressed
+ * COMPR_ZLIB  - compressed with zlib
+ */
+enum {
+       COMPR_NONE      = 0,
+       COMPR_ZLIB      = 1,
+};
+
+/*
+ * Journal entries come in groups of 16.  First group contains unique
+ * entries, next groups contain one entry per level
+ *
+ * JE_FIRST    - smallest possible journal entry number
+ *
+ * JEG_BASE    - base group, containing unique entries
+ * JE_COMMIT   - commit entry, validates all previous entries
+ * JE_DYNSB    - dynamic superblock, anything that ought to be in the
+ *               superblock but cannot because it is read-write data
+ * JE_ANCHOR   - anchor aka master inode aka inode file's inode
+ * JE_ERASECOUNT  erasecounts for all journal segments
+ * JE_SPILLOUT - unused
+ * JE_SEG_ALIAS        - aliases segments
+ * JE_AREA     - area description
+ *
+ * JE_LAST     - largest possible journal entry number
+ */
+enum {
+       JE_FIRST        = 0x01,
+
+       JEG_BASE        = 0x00,
+       JE_COMMIT       = 0x02,
+       JE_DYNSB        = 0x03,
+       JE_ANCHOR       = 0x04,
+       JE_ERASECOUNT   = 0x05,
+       JE_SPILLOUT     = 0x06,
+       JE_OBJ_ALIAS    = 0x0d,
+       JE_AREA         = 0x0e,
+
+       JE_LAST         = 0x0e,
+};
+
+#endif
diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c
new file mode 100644 (file)
index 0000000..7a23b3e
--- /dev/null
@@ -0,0 +1,2246 @@
+/*
+ * fs/logfs/readwrite.c
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ *
+ *
+ * Actually contains five sets of very similar functions:
+ * read                read blocks from a file
+ * seek_hole   find next hole
+ * seek_data   find next data block
+ * valid       check whether a block still belongs to a file
+ * write       write blocks to a file
+ * delete      delete a block (for directories and ifile)
+ * rewrite     move existing blocks of a file to a new location (gc helper)
+ * truncate    truncate a file
+ */
+#include "logfs.h"
+#include <linux/sched.h>
+
+static u64 adjust_bix(u64 bix, level_t level)
+{
+       switch (level) {
+       case 0:
+               return bix;
+       case LEVEL(1):
+               return max_t(u64, bix, I0_BLOCKS);
+       case LEVEL(2):
+               return max_t(u64, bix, I1_BLOCKS);
+       case LEVEL(3):
+               return max_t(u64, bix, I2_BLOCKS);
+       case LEVEL(4):
+               return max_t(u64, bix, I3_BLOCKS);
+       case LEVEL(5):
+               return max_t(u64, bix, I4_BLOCKS);
+       default:
+               WARN_ON(1);
+               return bix;
+       }
+}
+
+static inline u64 maxbix(u8 height)
+{
+       return 1ULL << (LOGFS_BLOCK_BITS * height);
+}
+
+/**
+ * The inode address space is cut in two halves.  Lower half belongs to data
+ * pages, upper half to indirect blocks.  If the high bit (INDIRECT_BIT) is
+ * set, the actual block index (bix) and level can be derived from the page
+ * index.
+ *
+ * The lowest three bits of the block index are set to 0 after packing and
+ * unpacking.  Since the lowest n bits (9 for 4KiB blocksize) are ignored
+ * anyway this is harmless.
+ */
+#define ARCH_SHIFT     (BITS_PER_LONG - 32)
+#define INDIRECT_BIT   (0x80000000UL << ARCH_SHIFT)
+#define LEVEL_SHIFT    (28 + ARCH_SHIFT)
+static inline pgoff_t first_indirect_block(void)
+{
+       return INDIRECT_BIT | (1ULL << LEVEL_SHIFT);
+}
+
+pgoff_t logfs_pack_index(u64 bix, level_t level)
+{
+       pgoff_t index;
+
+       BUG_ON(bix >= INDIRECT_BIT);
+       if (level == 0)
+               return bix;
+
+       index  = INDIRECT_BIT;
+       index |= (__force long)level << LEVEL_SHIFT;
+       index |= bix >> ((__force u8)level * LOGFS_BLOCK_BITS);
+       return index;
+}
+
+void logfs_unpack_index(pgoff_t index, u64 *bix, level_t *level)
+{
+       u8 __level;
+
+       if (!(index & INDIRECT_BIT)) {
+               *bix = index;
+               *level = 0;
+               return;
+       }
+
+       __level = (index & ~INDIRECT_BIT) >> LEVEL_SHIFT;
+       *level = LEVEL(__level);
+       *bix = (index << (__level * LOGFS_BLOCK_BITS)) & ~INDIRECT_BIT;
+       *bix = adjust_bix(*bix, *level);
+       return;
+}
+#undef ARCH_SHIFT
+#undef INDIRECT_BIT
+#undef LEVEL_SHIFT
+
+/*
+ * Time is stored as nanoseconds since the epoch.
+ */
+static struct timespec be64_to_timespec(__be64 betime)
+{
+       return ns_to_timespec(be64_to_cpu(betime));
+}
+
+static __be64 timespec_to_be64(struct timespec tsp)
+{
+       return cpu_to_be64((u64)tsp.tv_sec * NSEC_PER_SEC + tsp.tv_nsec);
+}
+
+static void logfs_disk_to_inode(struct logfs_disk_inode *di, struct inode*inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       int i;
+
+       inode->i_mode   = be16_to_cpu(di->di_mode);
+       li->li_height   = di->di_height;
+       li->li_flags    = be32_to_cpu(di->di_flags);
+       inode->i_uid    = be32_to_cpu(di->di_uid);
+       inode->i_gid    = be32_to_cpu(di->di_gid);
+       inode->i_size   = be64_to_cpu(di->di_size);
+       logfs_set_blocks(inode, be64_to_cpu(di->di_used_bytes));
+       inode->i_atime  = be64_to_timespec(di->di_atime);
+       inode->i_ctime  = be64_to_timespec(di->di_ctime);
+       inode->i_mtime  = be64_to_timespec(di->di_mtime);
+       inode->i_nlink  = be32_to_cpu(di->di_refcount);
+       inode->i_generation = be32_to_cpu(di->di_generation);
+
+       switch (inode->i_mode & S_IFMT) {
+       case S_IFSOCK:  /* fall through */
+       case S_IFBLK:   /* fall through */
+       case S_IFCHR:   /* fall through */
+       case S_IFIFO:
+               inode->i_rdev = be64_to_cpu(di->di_data[0]);
+               break;
+       case S_IFDIR:   /* fall through */
+       case S_IFREG:   /* fall through */
+       case S_IFLNK:
+               for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
+                       li->li_data[i] = be64_to_cpu(di->di_data[i]);
+               break;
+       default:
+               BUG();
+       }
+}
+
+static void logfs_inode_to_disk(struct inode *inode, struct logfs_disk_inode*di)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       int i;
+
+       di->di_mode     = cpu_to_be16(inode->i_mode);
+       di->di_height   = li->li_height;
+       di->di_pad      = 0;
+       di->di_flags    = cpu_to_be32(li->li_flags);
+       di->di_uid      = cpu_to_be32(inode->i_uid);
+       di->di_gid      = cpu_to_be32(inode->i_gid);
+       di->di_size     = cpu_to_be64(i_size_read(inode));
+       di->di_used_bytes = cpu_to_be64(li->li_used_bytes);
+       di->di_atime    = timespec_to_be64(inode->i_atime);
+       di->di_ctime    = timespec_to_be64(inode->i_ctime);
+       di->di_mtime    = timespec_to_be64(inode->i_mtime);
+       di->di_refcount = cpu_to_be32(inode->i_nlink);
+       di->di_generation = cpu_to_be32(inode->i_generation);
+
+       switch (inode->i_mode & S_IFMT) {
+       case S_IFSOCK:  /* fall through */
+       case S_IFBLK:   /* fall through */
+       case S_IFCHR:   /* fall through */
+       case S_IFIFO:
+               di->di_data[0] = cpu_to_be64(inode->i_rdev);
+               break;
+       case S_IFDIR:   /* fall through */
+       case S_IFREG:   /* fall through */
+       case S_IFLNK:
+               for (i = 0; i < LOGFS_EMBEDDED_FIELDS; i++)
+                       di->di_data[i] = cpu_to_be64(li->li_data[i]);
+               break;
+       default:
+               BUG();
+       }
+}
+
+static void __logfs_set_blocks(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       struct logfs_inode *li = logfs_inode(inode);
+
+       inode->i_blocks = ULONG_MAX;
+       if (li->li_used_bytes >> sb->s_blocksize_bits < ULONG_MAX)
+               inode->i_blocks = ALIGN(li->li_used_bytes, 512) >> 9;
+}
+
+void logfs_set_blocks(struct inode *inode, u64 bytes)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       li->li_used_bytes = bytes;
+       __logfs_set_blocks(inode);
+}
+
+static void prelock_page(struct super_block *sb, struct page *page, int lock)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       BUG_ON(!PageLocked(page));
+       if (lock) {
+               BUG_ON(PagePreLocked(page));
+               SetPagePreLocked(page);
+       } else {
+               /* We are in GC path. */
+               if (PagePreLocked(page))
+                       super->s_lock_count++;
+               else
+                       SetPagePreLocked(page);
+       }
+}
+
+static void preunlock_page(struct super_block *sb, struct page *page, int lock)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       BUG_ON(!PageLocked(page));
+       if (lock)
+               ClearPagePreLocked(page);
+       else {
+               /* We are in GC path. */
+               BUG_ON(!PagePreLocked(page));
+               if (super->s_lock_count)
+                       super->s_lock_count--;
+               else
+                       ClearPagePreLocked(page);
+       }
+}
+
+/*
+ * Logfs is prone to an AB-BA deadlock where one task tries to acquire
+ * s_write_mutex with a locked page and GC tries to get that page while holding
+ * s_write_mutex.
+ * To solve this issue logfs will ignore the page lock iff the page in question
+ * is waiting for s_write_mutex.  We annotate this fact by setting PG_pre_locked
+ * in addition to PG_locked.
+ */
+static void logfs_get_wblocks(struct super_block *sb, struct page *page,
+               int lock)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       if (page)
+               prelock_page(sb, page, lock);
+
+       if (lock) {
+               mutex_lock(&super->s_write_mutex);
+               logfs_gc_pass(sb);
+               /* FIXME: We also have to check for shadowed space
+                * and mempool fill grade */
+       }
+}
+
+static void logfs_put_wblocks(struct super_block *sb, struct page *page,
+               int lock)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       if (page)
+               preunlock_page(sb, page, lock);
+       /* Order matters - we must clear PG_pre_locked before releasing
+        * s_write_mutex or we could race against another task. */
+       if (lock)
+               mutex_unlock(&super->s_write_mutex);
+}
+
+static struct page *logfs_get_read_page(struct inode *inode, u64 bix,
+               level_t level)
+{
+       return find_or_create_page(inode->i_mapping,
+                       logfs_pack_index(bix, level), GFP_NOFS);
+}
+
+static void logfs_put_read_page(struct page *page)
+{
+       unlock_page(page);
+       page_cache_release(page);
+}
+
+static void logfs_lock_write_page(struct page *page)
+{
+       int loop = 0;
+
+       while (unlikely(!trylock_page(page))) {
+               if (loop++ > 0x1000) {
+                       /* Has been observed once so far... */
+                       printk(KERN_ERR "stack at %p\n", &loop);
+                       BUG();
+               }
+               if (PagePreLocked(page)) {
+                       /* Holder of page lock is waiting for us, it
+                        * is safe to use this page. */
+                       break;
+               }
+               /* Some other process has this page locked and has
+                * nothing to do with us.  Wait for it to finish.
+                */
+               schedule();
+       }
+       BUG_ON(!PageLocked(page));
+}
+
+static struct page *logfs_get_write_page(struct inode *inode, u64 bix,
+               level_t level)
+{
+       struct address_space *mapping = inode->i_mapping;
+       pgoff_t index = logfs_pack_index(bix, level);
+       struct page *page;
+       int err;
+
+repeat:
+       page = find_get_page(mapping, index);
+       if (!page) {
+               page = __page_cache_alloc(GFP_NOFS);
+               if (!page)
+                       return NULL;
+               err = add_to_page_cache_lru(page, mapping, index, GFP_NOFS);
+               if (unlikely(err)) {
+                       page_cache_release(page);
+                       if (err == -EEXIST)
+                               goto repeat;
+                       return NULL;
+               }
+       } else logfs_lock_write_page(page);
+       BUG_ON(!PageLocked(page));
+       return page;
+}
+
+static void logfs_unlock_write_page(struct page *page)
+{
+       if (!PagePreLocked(page))
+               unlock_page(page);
+}
+
+static void logfs_put_write_page(struct page *page)
+{
+       logfs_unlock_write_page(page);
+       page_cache_release(page);
+}
+
+static struct page *logfs_get_page(struct inode *inode, u64 bix, level_t level,
+               int rw)
+{
+       if (rw == READ)
+               return logfs_get_read_page(inode, bix, level);
+       else
+               return logfs_get_write_page(inode, bix, level);
+}
+
+static void logfs_put_page(struct page *page, int rw)
+{
+       if (rw == READ)
+               logfs_put_read_page(page);
+       else
+               logfs_put_write_page(page);
+}
+
+static unsigned long __get_bits(u64 val, int skip, int no)
+{
+       u64 ret = val;
+
+       ret >>= skip * no;
+       ret <<= 64 - no;
+       ret >>= 64 - no;
+       return ret;
+}
+
+static unsigned long get_bits(u64 val, level_t skip)
+{
+       return __get_bits(val, (__force int)skip, LOGFS_BLOCK_BITS);
+}
+
+static inline void init_shadow_tree(struct super_block *sb,
+               struct shadow_tree *tree)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       btree_init_mempool64(&tree->new, super->s_btree_pool);
+       btree_init_mempool64(&tree->old, super->s_btree_pool);
+}
+
+static void indirect_write_block(struct logfs_block *block)
+{
+       struct page *page;
+       struct inode *inode;
+       int ret;
+
+       page = block->page;
+       inode = page->mapping->host;
+       logfs_lock_write_page(page);
+       ret = logfs_write_buf(inode, page, 0);
+       logfs_unlock_write_page(page);
+       /*
+        * This needs some rework.  Unless you want your filesystem to run
+        * completely synchronously (you don't), the filesystem will always
+        * report writes as 'successful' before the actual work has been
+        * done.  The actual work gets done here and this is where any errors
+        * will show up.  And there isn't much we can do about it, really.
+        *
+        * Some attempts to fix the errors (move from bad blocks, retry io,...)
+        * have already been done, so anything left should be either a broken
+        * device or a bug somewhere in logfs itself.  Being relatively new,
+        * the odds currently favor a bug, so for now the line below isn't
+        * entirely tasteles.
+        */
+       BUG_ON(ret);
+}
+
+static void inode_write_block(struct logfs_block *block)
+{
+       struct inode *inode;
+       int ret;
+
+       inode = block->inode;
+       if (inode->i_ino == LOGFS_INO_MASTER)
+               logfs_write_anchor(inode->i_sb);
+       else {
+               ret = __logfs_write_inode(inode, 0);
+               /* see indirect_write_block comment */
+               BUG_ON(ret);
+       }
+}
+
+static gc_level_t inode_block_level(struct logfs_block *block)
+{
+       BUG_ON(block->inode->i_ino == LOGFS_INO_MASTER);
+       return GC_LEVEL(LOGFS_MAX_LEVELS);
+}
+
+static gc_level_t indirect_block_level(struct logfs_block *block)
+{
+       struct page *page;
+       struct inode *inode;
+       u64 bix;
+       level_t level;
+
+       page = block->page;
+       inode = page->mapping->host;
+       logfs_unpack_index(page->index, &bix, &level);
+       return expand_level(inode->i_ino, level);
+}
+
+/*
+ * This silences a false, yet annoying gcc warning.  I hate it when my editor
+ * jumps into bitops.h each time I recompile this file.
+ * TODO: Complain to gcc folks about this and upgrade compiler.
+ */
+static unsigned long fnb(const unsigned long *addr,
+               unsigned long size, unsigned long offset)
+{
+       return find_next_bit(addr, size, offset);
+}
+
+static __be64 inode_val0(struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       u64 val;
+
+       /*
+        * Explicit shifting generates good code, but must match the format
+        * of the structure.  Add some paranoia just in case.
+        */
+       BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_mode) != 0);
+       BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_height) != 2);
+       BUILD_BUG_ON(offsetof(struct logfs_disk_inode, di_flags) != 4);
+
+       val =   (u64)inode->i_mode << 48 |
+               (u64)li->li_height << 40 |
+               (u64)li->li_flags;
+       return cpu_to_be64(val);
+}
+
+static int inode_write_alias(struct super_block *sb,
+               struct logfs_block *block, write_alias_t *write_one_alias)
+{
+       struct inode *inode = block->inode;
+       struct logfs_inode *li = logfs_inode(inode);
+       unsigned long pos;
+       u64 ino , bix;
+       __be64 val;
+       level_t level;
+       int err;
+
+       for (pos = 0; ; pos++) {
+               pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);
+               if (pos >= LOGFS_EMBEDDED_FIELDS + INODE_POINTER_OFS)
+                       return 0;
+
+               switch (pos) {
+               case INODE_HEIGHT_OFS:
+                       val = inode_val0(inode);
+                       break;
+               case INODE_USED_OFS:
+                       val = cpu_to_be64(li->li_used_bytes);;
+                       break;
+               case INODE_SIZE_OFS:
+                       val = cpu_to_be64(i_size_read(inode));
+                       break;
+               case INODE_POINTER_OFS ... INODE_POINTER_OFS + LOGFS_EMBEDDED_FIELDS - 1:
+                       val = cpu_to_be64(li->li_data[pos - INODE_POINTER_OFS]);
+                       break;
+               default:
+                       BUG();
+               }
+
+               ino = LOGFS_INO_MASTER;
+               bix = inode->i_ino;
+               level = LEVEL(0);
+               err = write_one_alias(sb, ino, bix, level, pos, val);
+               if (err)
+                       return err;
+       }
+}
+
+static int indirect_write_alias(struct super_block *sb,
+               struct logfs_block *block, write_alias_t *write_one_alias)
+{
+       unsigned long pos;
+       struct page *page = block->page;
+       u64 ino , bix;
+       __be64 *child, val;
+       level_t level;
+       int err;
+
+       for (pos = 0; ; pos++) {
+               pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);
+               if (pos >= LOGFS_BLOCK_FACTOR)
+                       return 0;
+
+               ino = page->mapping->host->i_ino;
+               logfs_unpack_index(page->index, &bix, &level);
+               child = kmap_atomic(page, KM_USER0);
+               val = child[pos];
+               kunmap_atomic(child, KM_USER0);
+               err = write_one_alias(sb, ino, bix, level, pos, val);
+               if (err)
+                       return err;
+       }
+}
+
+int logfs_write_obj_aliases_pagecache(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_block *block;
+       int err;
+
+       list_for_each_entry(block, &super->s_object_alias, alias_list) {
+               err = block->ops->write_alias(sb, block, write_alias_journal);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+void __free_block(struct super_block *sb, struct logfs_block *block)
+{
+       BUG_ON(!list_empty(&block->item_list));
+       list_del(&block->alias_list);
+       mempool_free(block, logfs_super(sb)->s_block_pool);
+}
+
+static void inode_free_block(struct super_block *sb, struct logfs_block *block)
+{
+       struct inode *inode = block->inode;
+
+       logfs_inode(inode)->li_block = NULL;
+       __free_block(sb, block);
+}
+
+static void indirect_free_block(struct super_block *sb,
+               struct logfs_block *block)
+{
+       ClearPagePrivate(block->page);
+       block->page->private = 0;
+       __free_block(sb, block);
+}
+
+
+static struct logfs_block_ops inode_block_ops = {
+       .write_block = inode_write_block,
+       .block_level = inode_block_level,
+       .free_block = inode_free_block,
+       .write_alias = inode_write_alias,
+};
+
+struct logfs_block_ops indirect_block_ops = {
+       .write_block = indirect_write_block,
+       .block_level = indirect_block_level,
+       .free_block = indirect_free_block,
+       .write_alias = indirect_write_alias,
+};
+
+struct logfs_block *__alloc_block(struct super_block *sb,
+               u64 ino, u64 bix, level_t level)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_block *block;
+
+       block = mempool_alloc(super->s_block_pool, GFP_NOFS);
+       memset(block, 0, sizeof(*block));
+       INIT_LIST_HEAD(&block->alias_list);
+       INIT_LIST_HEAD(&block->item_list);
+       block->sb = sb;
+       block->ino = ino;
+       block->bix = bix;
+       block->level = level;
+       return block;
+}
+
+static void alloc_inode_block(struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct logfs_block *block;
+
+       if (li->li_block)
+               return;
+
+       block = __alloc_block(inode->i_sb, LOGFS_INO_MASTER, inode->i_ino, 0);
+       block->inode = inode;
+       li->li_block = block;
+       block->ops = &inode_block_ops;
+}
+
+void initialize_block_counters(struct page *page, struct logfs_block *block,
+               __be64 *array, int page_is_empty)
+{
+       u64 ptr;
+       int i, start;
+
+       block->partial = 0;
+       block->full = 0;
+       start = 0;
+       if (page->index < first_indirect_block()) {
+               /* Counters are pointless on level 0 */
+               return;
+       }
+       if (page->index == first_indirect_block()) {
+               /* Skip unused pointers */
+               start = I0_BLOCKS;
+               block->full = I0_BLOCKS;
+       }
+       if (!page_is_empty) {
+               for (i = start; i < LOGFS_BLOCK_FACTOR; i++) {
+                       ptr = be64_to_cpu(array[i]);
+                       if (ptr)
+                               block->partial++;
+                       if (ptr & LOGFS_FULLY_POPULATED)
+                               block->full++;
+               }
+       }
+}
+
+static void alloc_data_block(struct inode *inode, struct page *page)
+{
+       struct logfs_block *block;
+       u64 bix;
+       level_t level;
+
+       if (PagePrivate(page))
+               return;
+
+       logfs_unpack_index(page->index, &bix, &level);
+       block = __alloc_block(inode->i_sb, inode->i_ino, bix, level);
+       block->page = page;
+       SetPagePrivate(page);
+       page->private = (unsigned long)block;
+       block->ops = &indirect_block_ops;
+}
+
+static void alloc_indirect_block(struct inode *inode, struct page *page,
+               int page_is_empty)
+{
+       struct logfs_block *block;
+       __be64 *array;
+
+       if (PagePrivate(page))
+               return;
+
+       alloc_data_block(inode, page);
+
+       block = logfs_block(page);
+       array = kmap_atomic(page, KM_USER0);
+       initialize_block_counters(page, block, array, page_is_empty);
+       kunmap_atomic(array, KM_USER0);
+}
+
+static void block_set_pointer(struct page *page, int index, u64 ptr)
+{
+       struct logfs_block *block = logfs_block(page);
+       __be64 *array;
+       u64 oldptr;
+
+       BUG_ON(!block);
+       array = kmap_atomic(page, KM_USER0);
+       oldptr = be64_to_cpu(array[index]);
+       array[index] = cpu_to_be64(ptr);
+       kunmap_atomic(array, KM_USER0);
+       SetPageUptodate(page);
+
+       block->full += !!(ptr & LOGFS_FULLY_POPULATED)
+               - !!(oldptr & LOGFS_FULLY_POPULATED);
+       block->partial += !!ptr - !!oldptr;
+}
+
+static u64 block_get_pointer(struct page *page, int index)
+{
+       __be64 *block;
+       u64 ptr;
+
+       block = kmap_atomic(page, KM_USER0);
+       ptr = be64_to_cpu(block[index]);
+       kunmap_atomic(block, KM_USER0);
+       return ptr;
+}
+
+static int logfs_read_empty(struct page *page)
+{
+       zero_user_segment(page, 0, PAGE_CACHE_SIZE);
+       return 0;
+}
+
+static int logfs_read_direct(struct inode *inode, struct page *page)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       pgoff_t index = page->index;
+       u64 block;
+
+       block = li->li_data[index];
+       if (!block)
+               return logfs_read_empty(page);
+
+       return logfs_segment_read(inode, page, block, index, 0);
+}
+
+static int logfs_read_loop(struct inode *inode, struct page *page,
+               int rw_context)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       u64 bix, bofs = li->li_data[INDIRECT_INDEX];
+       level_t level, target_level;
+       int ret;
+       struct page *ipage;
+
+       logfs_unpack_index(page->index, &bix, &target_level);
+       if (!bofs)
+               return logfs_read_empty(page);
+
+       if (bix >= maxbix(li->li_height))
+               return logfs_read_empty(page);
+
+       for (level = LEVEL(li->li_height);
+                       (__force u8)level > (__force u8)target_level;
+                       level = SUBLEVEL(level)){
+               ipage = logfs_get_page(inode, bix, level, rw_context);
+               if (!ipage)
+                       return -ENOMEM;
+
+               ret = logfs_segment_read(inode, ipage, bofs, bix, level);
+               if (ret) {
+                       logfs_put_read_page(ipage);
+                       return ret;
+               }
+
+               bofs = block_get_pointer(ipage, get_bits(bix, SUBLEVEL(level)));
+               logfs_put_page(ipage, rw_context);
+               if (!bofs)
+                       return logfs_read_empty(page);
+       }
+
+       return logfs_segment_read(inode, page, bofs, bix, 0);
+}
+
+static int logfs_read_block(struct inode *inode, struct page *page,
+               int rw_context)
+{
+       pgoff_t index = page->index;
+
+       if (index < I0_BLOCKS)
+               return logfs_read_direct(inode, page);
+       return logfs_read_loop(inode, page, rw_context);
+}
+
+static int logfs_exist_loop(struct inode *inode, u64 bix)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       u64 bofs = li->li_data[INDIRECT_INDEX];
+       level_t level;
+       int ret;
+       struct page *ipage;
+
+       if (!bofs)
+               return 0;
+       if (bix >= maxbix(li->li_height))
+               return 0;
+
+       for (level = LEVEL(li->li_height); level != 0; level = SUBLEVEL(level)) {
+               ipage = logfs_get_read_page(inode, bix, level);
+               if (!ipage)
+                       return -ENOMEM;
+
+               ret = logfs_segment_read(inode, ipage, bofs, bix, level);
+               if (ret) {
+                       logfs_put_read_page(ipage);
+                       return ret;
+               }
+
+               bofs = block_get_pointer(ipage, get_bits(bix, SUBLEVEL(level)));
+               logfs_put_read_page(ipage);
+               if (!bofs)
+                       return 0;
+       }
+
+       return 1;
+}
+
+int logfs_exist_block(struct inode *inode, u64 bix)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       if (bix < I0_BLOCKS)
+               return !!li->li_data[bix];
+       return logfs_exist_loop(inode, bix);
+}
+
+static u64 seek_holedata_direct(struct inode *inode, u64 bix, int data)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       for (; bix < I0_BLOCKS; bix++)
+               if (data ^ (li->li_data[bix] == 0))
+                       return bix;
+       return I0_BLOCKS;
+}
+
+static u64 seek_holedata_loop(struct inode *inode, u64 bix, int data)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       __be64 *rblock;
+       u64 increment, bofs = li->li_data[INDIRECT_INDEX];
+       level_t level;
+       int ret, slot;
+       struct page *page;
+
+       BUG_ON(!bofs);
+
+       for (level = LEVEL(li->li_height); level != 0; level = SUBLEVEL(level)) {
+               increment = 1 << (LOGFS_BLOCK_BITS * ((__force u8)level-1));
+               page = logfs_get_read_page(inode, bix, level);
+               if (!page)
+                       return bix;
+
+               ret = logfs_segment_read(inode, page, bofs, bix, level);
+               if (ret) {
+                       logfs_put_read_page(page);
+                       return bix;
+               }
+
+               slot = get_bits(bix, SUBLEVEL(level));
+               rblock = kmap_atomic(page, KM_USER0);
+               while (slot < LOGFS_BLOCK_FACTOR) {
+                       if (data && (rblock[slot] != 0))
+                               break;
+                       if (!data && !(be64_to_cpu(rblock[slot]) & LOGFS_FULLY_POPULATED))
+                               break;
+                       slot++;
+                       bix += increment;
+                       bix &= ~(increment - 1);
+               }
+               if (slot >= LOGFS_BLOCK_FACTOR) {
+                       kunmap_atomic(rblock, KM_USER0);
+                       logfs_put_read_page(page);
+                       return bix;
+               }
+               bofs = be64_to_cpu(rblock[slot]);
+               kunmap_atomic(rblock, KM_USER0);
+               logfs_put_read_page(page);
+               if (!bofs) {
+                       BUG_ON(data);
+                       return bix;
+               }
+       }
+       return bix;
+}
+
+/**
+ * logfs_seek_hole - find next hole starting at a given block index
+ * @inode:             inode to search in
+ * @bix:               block index to start searching
+ *
+ * Returns next hole.  If the file doesn't contain any further holes, the
+ * block address next to eof is returned instead.
+ */
+u64 logfs_seek_hole(struct inode *inode, u64 bix)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       if (bix < I0_BLOCKS) {
+               bix = seek_holedata_direct(inode, bix, 0);
+               if (bix < I0_BLOCKS)
+                       return bix;
+       }
+
+       if (!li->li_data[INDIRECT_INDEX])
+               return bix;
+       else if (li->li_data[INDIRECT_INDEX] & LOGFS_FULLY_POPULATED)
+               bix = maxbix(li->li_height);
+       else {
+               bix = seek_holedata_loop(inode, bix, 0);
+               if (bix < maxbix(li->li_height))
+                       return bix;
+               /* Should not happen anymore.  But if some port writes semi-
+                * corrupt images (as this one used to) we might run into it.
+                */
+               WARN_ON_ONCE(bix == maxbix(li->li_height));
+       }
+
+       return bix;
+}
+
+static u64 __logfs_seek_data(struct inode *inode, u64 bix)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       if (bix < I0_BLOCKS) {
+               bix = seek_holedata_direct(inode, bix, 1);
+               if (bix < I0_BLOCKS)
+                       return bix;
+       }
+
+       if (bix < maxbix(li->li_height)) {
+               if (!li->li_data[INDIRECT_INDEX])
+                       bix = maxbix(li->li_height);
+               else
+                       return seek_holedata_loop(inode, bix, 1);
+       }
+
+       return bix;
+}
+
+/**
+ * logfs_seek_data - find next data block after a given block index
+ * @inode:             inode to search in
+ * @bix:               block index to start searching
+ *
+ * Returns next data block.  If the file doesn't contain any further data
+ * blocks, the last block in the file is returned instead.
+ */
+u64 logfs_seek_data(struct inode *inode, u64 bix)
+{
+       struct super_block *sb = inode->i_sb;
+       u64 ret, end;
+
+       ret = __logfs_seek_data(inode, bix);
+       end = i_size_read(inode) >> sb->s_blocksize_bits;
+       if (ret >= end)
+               ret = max(bix, end);
+       return ret;
+}
+
+static int logfs_is_valid_direct(struct logfs_inode *li, u64 bix, u64 ofs)
+{
+       return pure_ofs(li->li_data[bix]) == ofs;
+}
+
+static int __logfs_is_valid_loop(struct inode *inode, u64 bix,
+               u64 ofs, u64 bofs)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       level_t level;
+       int ret;
+       struct page *page;
+
+       for (level = LEVEL(li->li_height); level != 0; level = SUBLEVEL(level)){
+               page = logfs_get_write_page(inode, bix, level);
+               BUG_ON(!page);
+
+               ret = logfs_segment_read(inode, page, bofs, bix, level);
+               if (ret) {
+                       logfs_put_write_page(page);
+                       return 0;
+               }
+
+               bofs = block_get_pointer(page, get_bits(bix, SUBLEVEL(level)));
+               logfs_put_write_page(page);
+               if (!bofs)
+                       return 0;
+
+               if (pure_ofs(bofs) == ofs)
+                       return 1;
+       }
+       return 0;
+}
+
+static int logfs_is_valid_loop(struct inode *inode, u64 bix, u64 ofs)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       u64 bofs = li->li_data[INDIRECT_INDEX];
+
+       if (!bofs)
+               return 0;
+
+       if (bix >= maxbix(li->li_height))
+               return 0;
+
+       if (pure_ofs(bofs) == ofs)
+               return 1;
+
+       return __logfs_is_valid_loop(inode, bix, ofs, bofs);
+}
+
+static int __logfs_is_valid_block(struct inode *inode, u64 bix, u64 ofs)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       if ((inode->i_nlink == 0) && atomic_read(&inode->i_count) == 1)
+               return 0;
+
+       if (bix < I0_BLOCKS)
+               return logfs_is_valid_direct(li, bix, ofs);
+       return logfs_is_valid_loop(inode, bix, ofs);
+}
+
+/**
+ * logfs_is_valid_block - check whether this block is still valid
+ *
+ * @sb - superblock
+ * @ofs        - block physical offset
+ * @ino        - block inode number
+ * @bix        - block index
+ * @level - block level
+ *
+ * Returns 0 if the block is invalid, 1 if it is valid and 2 if it will
+ * become invalid once the journal is written.
+ */
+int logfs_is_valid_block(struct super_block *sb, u64 ofs, u64 ino, u64 bix,
+               gc_level_t gc_level)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *inode;
+       int ret, cookie;
+
+       /* Umount closes a segment with free blocks remaining.  Those
+        * blocks are by definition invalid. */
+       if (ino == -1)
+               return 0;
+
+       LOGFS_BUG_ON((u64)(u_long)ino != ino, sb);
+
+       inode = logfs_safe_iget(sb, ino, &cookie);
+       if (IS_ERR(inode))
+               goto invalid;
+
+       ret = __logfs_is_valid_block(inode, bix, ofs);
+       logfs_safe_iput(inode, cookie);
+       if (ret)
+               return ret;
+
+invalid:
+       /* Block is nominally invalid, but may still sit in the shadow tree,
+        * waiting for a journal commit.
+        */
+       if (btree_lookup64(&super->s_shadow_tree.old, ofs))
+               return 2;
+       return 0;
+}
+
+int logfs_readpage_nolock(struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       int ret = -EIO;
+
+       ret = logfs_read_block(inode, page, READ);
+
+       if (ret) {
+               ClearPageUptodate(page);
+               SetPageError(page);
+       } else {
+               SetPageUptodate(page);
+               ClearPageError(page);
+       }
+       flush_dcache_page(page);
+
+       return ret;
+}
+
+static int logfs_reserve_bytes(struct inode *inode, int bytes)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+       u64 available = super->s_free_bytes + super->s_dirty_free_bytes
+                       - super->s_dirty_used_bytes - super->s_dirty_pages;
+
+       if (!bytes)
+               return 0;
+
+       if (available < bytes)
+               return -ENOSPC;
+
+       if (available < bytes + super->s_root_reserve &&
+                       !capable(CAP_SYS_RESOURCE))
+               return -ENOSPC;
+
+       return 0;
+}
+
+int get_page_reserve(struct inode *inode, struct page *page)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+       int ret;
+
+       if (logfs_block(page) && logfs_block(page)->reserved_bytes)
+               return 0;
+
+       logfs_get_wblocks(inode->i_sb, page, WF_LOCK);
+       ret = logfs_reserve_bytes(inode, 6 * LOGFS_MAX_OBJECTSIZE);
+       if (!ret) {
+               alloc_data_block(inode, page);
+               logfs_block(page)->reserved_bytes += 6 * LOGFS_MAX_OBJECTSIZE;
+               super->s_dirty_pages += 6 * LOGFS_MAX_OBJECTSIZE;
+       }
+       logfs_put_wblocks(inode->i_sb, page, WF_LOCK);
+       return ret;
+}
+
+/*
+ * We are protected by write lock.  Push victims up to superblock level
+ * and release transaction when appropriate.
+ */
+/* FIXME: This is currently called from the wrong spots. */
+static void logfs_handle_transaction(struct inode *inode,
+               struct logfs_transaction *ta)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+
+       if (!ta)
+               return;
+       logfs_inode(inode)->li_block->ta = NULL;
+
+       if (inode->i_ino != LOGFS_INO_MASTER) {
+               BUG(); /* FIXME: Yes, this needs more thought */
+               /* just remember the transaction until inode is written */
+               //BUG_ON(logfs_inode(inode)->li_transaction);
+               //logfs_inode(inode)->li_transaction = ta;
+               return;
+       }
+
+       switch (ta->state) {
+       case CREATE_1: /* fall through */
+       case UNLINK_1:
+               BUG_ON(super->s_victim_ino);
+               super->s_victim_ino = ta->ino;
+               break;
+       case CREATE_2: /* fall through */
+       case UNLINK_2:
+               BUG_ON(super->s_victim_ino != ta->ino);
+               super->s_victim_ino = 0;
+               /* transaction ends here - free it */
+               kfree(ta);
+               break;
+       case CROSS_RENAME_1:
+               BUG_ON(super->s_rename_dir);
+               BUG_ON(super->s_rename_pos);
+               super->s_rename_dir = ta->dir;
+               super->s_rename_pos = ta->pos;
+               break;
+       case CROSS_RENAME_2:
+               BUG_ON(super->s_rename_dir != ta->dir);
+               BUG_ON(super->s_rename_pos != ta->pos);
+               super->s_rename_dir = 0;
+               super->s_rename_pos = 0;
+               kfree(ta);
+               break;
+       case TARGET_RENAME_1:
+               BUG_ON(super->s_rename_dir);
+               BUG_ON(super->s_rename_pos);
+               BUG_ON(super->s_victim_ino);
+               super->s_rename_dir = ta->dir;
+               super->s_rename_pos = ta->pos;
+               super->s_victim_ino = ta->ino;
+               break;
+       case TARGET_RENAME_2:
+               BUG_ON(super->s_rename_dir != ta->dir);
+               BUG_ON(super->s_rename_pos != ta->pos);
+               BUG_ON(super->s_victim_ino != ta->ino);
+               super->s_rename_dir = 0;
+               super->s_rename_pos = 0;
+               break;
+       case TARGET_RENAME_3:
+               BUG_ON(super->s_rename_dir);
+               BUG_ON(super->s_rename_pos);
+               BUG_ON(super->s_victim_ino != ta->ino);
+               super->s_victim_ino = 0;
+               kfree(ta);
+               break;
+       default:
+               BUG();
+       }
+}
+
+/*
+ * Not strictly a reservation, but rather a check that we still have enough
+ * space to satisfy the write.
+ */
+static int logfs_reserve_blocks(struct inode *inode, int blocks)
+{
+       return logfs_reserve_bytes(inode, blocks * LOGFS_MAX_OBJECTSIZE);
+}
+
+struct write_control {
+       u64 ofs;
+       long flags;
+};
+
+static struct logfs_shadow *alloc_shadow(struct inode *inode, u64 bix,
+               level_t level, u64 old_ofs)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+       struct logfs_shadow *shadow;
+
+       shadow = mempool_alloc(super->s_shadow_pool, GFP_NOFS);
+       memset(shadow, 0, sizeof(*shadow));
+       shadow->ino = inode->i_ino;
+       shadow->bix = bix;
+       shadow->gc_level = expand_level(inode->i_ino, level);
+       shadow->old_ofs = old_ofs & ~LOGFS_FULLY_POPULATED;
+       return shadow;
+}
+
+static void free_shadow(struct inode *inode, struct logfs_shadow *shadow)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+
+       mempool_free(shadow, super->s_shadow_pool);
+}
+
+/**
+ * fill_shadow_tree - Propagate shadow tree changes due to a write
+ * @inode:     Inode owning the page
+ * @page:      Struct page that was written
+ * @shadow:    Shadow for the current write
+ *
+ * Writes in logfs can result in two semi-valid objects.  The old object
+ * is still valid as long as it can be reached by following pointers on
+ * the medium.  Only when writes propagate all the way up to the journal
+ * has the new object safely replaced the old one.
+ *
+ * To handle this problem, a struct logfs_shadow is used to represent
+ * every single write.  It is attached to the indirect block, which is
+ * marked dirty.  When the indirect block is written, its shadows are
+ * handed up to the next indirect block (or inode).  Untimately they
+ * will reach the master inode and be freed upon journal commit.
+ *
+ * This function handles a single step in the propagation.  It adds the
+ * shadow for the current write to the tree, along with any shadows in
+ * the page's tree, in case it was an indirect block.  If a page is
+ * written, the inode parameter is left NULL, if an inode is written,
+ * the page parameter is left NULL.
+ */
+static void fill_shadow_tree(struct inode *inode, struct page *page,
+               struct logfs_shadow *shadow)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+       struct logfs_block *block = logfs_block(page);
+       struct shadow_tree *tree = &super->s_shadow_tree;
+
+       if (PagePrivate(page)) {
+               if (block->alias_map)
+                       super->s_no_object_aliases -= bitmap_weight(
+                                       block->alias_map, LOGFS_BLOCK_FACTOR);
+               logfs_handle_transaction(inode, block->ta);
+               block->ops->free_block(inode->i_sb, block);
+       }
+       if (shadow) {
+               if (shadow->old_ofs)
+                       btree_insert64(&tree->old, shadow->old_ofs, shadow,
+                                       GFP_NOFS);
+               else
+                       btree_insert64(&tree->new, shadow->new_ofs, shadow,
+                                       GFP_NOFS);
+
+               super->s_dirty_used_bytes += shadow->new_len;
+               super->s_dirty_free_bytes += shadow->old_len;
+       }
+}
+
+static void logfs_set_alias(struct super_block *sb, struct logfs_block *block,
+               long child_no)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       if (block->inode && block->inode->i_ino == LOGFS_INO_MASTER) {
+               /* Aliases in the master inode are pointless. */
+               return;
+       }
+
+       if (!test_bit(child_no, block->alias_map)) {
+               set_bit(child_no, block->alias_map);
+               super->s_no_object_aliases++;
+       }
+       list_move_tail(&block->alias_list, &super->s_object_alias);
+}
+
+/*
+ * Object aliases can and often do change the size and occupied space of a
+ * file.  So not only do we have to change the pointers, we also have to
+ * change inode->i_size and li->li_used_bytes.  Which is done by setting
+ * another two object aliases for the inode itself.
+ */
+static void set_iused(struct inode *inode, struct logfs_shadow *shadow)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       if (shadow->new_len == shadow->old_len)
+               return;
+
+       alloc_inode_block(inode);
+       li->li_used_bytes += shadow->new_len - shadow->old_len;
+       __logfs_set_blocks(inode);
+       logfs_set_alias(inode->i_sb, li->li_block, INODE_USED_OFS);
+       logfs_set_alias(inode->i_sb, li->li_block, INODE_SIZE_OFS);
+}
+
+static int logfs_write_i0(struct inode *inode, struct page *page,
+               struct write_control *wc)
+{
+       struct logfs_shadow *shadow;
+       u64 bix;
+       level_t level;
+       int full, err = 0;
+
+       logfs_unpack_index(page->index, &bix, &level);
+       if (wc->ofs == 0)
+               if (logfs_reserve_blocks(inode, 1))
+                       return -ENOSPC;
+
+       shadow = alloc_shadow(inode, bix, level, wc->ofs);
+       if (wc->flags & WF_WRITE)
+               err = logfs_segment_write(inode, page, shadow);
+       if (wc->flags & WF_DELETE)
+               logfs_segment_delete(inode, shadow);
+       if (err) {
+               free_shadow(inode, shadow);
+               return err;
+       }
+
+       set_iused(inode, shadow);
+       full = 1;
+       if (level != 0) {
+               alloc_indirect_block(inode, page, 0);
+               full = logfs_block(page)->full == LOGFS_BLOCK_FACTOR;
+       }
+       fill_shadow_tree(inode, page, shadow);
+       wc->ofs = shadow->new_ofs;
+       if (wc->ofs && full)
+               wc->ofs |= LOGFS_FULLY_POPULATED;
+       return 0;
+}
+
+static int logfs_write_direct(struct inode *inode, struct page *page,
+               long flags)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct write_control wc = {
+               .ofs = li->li_data[page->index],
+               .flags = flags,
+       };
+       int err;
+
+       alloc_inode_block(inode);
+
+       err = logfs_write_i0(inode, page, &wc);
+       if (err)
+               return err;
+
+       li->li_data[page->index] = wc.ofs;
+       logfs_set_alias(inode->i_sb, li->li_block,
+                       page->index + INODE_POINTER_OFS);
+       return 0;
+}
+
+static int ptr_change(u64 ofs, struct page *page)
+{
+       struct logfs_block *block = logfs_block(page);
+       int empty0, empty1, full0, full1;
+
+       empty0 = ofs == 0;
+       empty1 = block->partial == 0;
+       if (empty0 != empty1)
+               return 1;
+
+       /* The !! is necessary to shrink result to int */
+       full0 = !!(ofs & LOGFS_FULLY_POPULATED);
+       full1 = block->full == LOGFS_BLOCK_FACTOR;
+       if (full0 != full1)
+               return 1;
+       return 0;
+}
+
+static int __logfs_write_rec(struct inode *inode, struct page *page,
+               struct write_control *this_wc,
+               pgoff_t bix, level_t target_level, level_t level)
+{
+       int ret, page_empty = 0;
+       int child_no = get_bits(bix, SUBLEVEL(level));
+       struct page *ipage;
+       struct write_control child_wc = {
+               .flags = this_wc->flags,
+       };
+
+       ipage = logfs_get_write_page(inode, bix, level);
+       if (!ipage)
+               return -ENOMEM;
+
+       if (this_wc->ofs) {
+               ret = logfs_segment_read(inode, ipage, this_wc->ofs, bix, level);
+               if (ret)
+                       goto out;
+       } else if (!PageUptodate(ipage)) {
+               page_empty = 1;
+               logfs_read_empty(ipage);
+       }
+
+       child_wc.ofs = block_get_pointer(ipage, child_no);
+
+       if ((__force u8)level-1 > (__force u8)target_level)
+               ret = __logfs_write_rec(inode, page, &child_wc, bix,
+                               target_level, SUBLEVEL(level));
+       else
+               ret = logfs_write_i0(inode, page, &child_wc);
+
+       if (ret)
+               goto out;
+
+       alloc_indirect_block(inode, ipage, page_empty);
+       block_set_pointer(ipage, child_no, child_wc.ofs);
+       /* FIXME: first condition seems superfluous */
+       if (child_wc.ofs || logfs_block(ipage)->partial)
+               this_wc->flags |= WF_WRITE;
+       /* the condition on this_wc->ofs ensures that we won't consume extra
+        * space for indirect blocks in the future, which we cannot reserve */
+       if (!this_wc->ofs || ptr_change(this_wc->ofs, ipage))
+               ret = logfs_write_i0(inode, ipage, this_wc);
+       else
+               logfs_set_alias(inode->i_sb, logfs_block(ipage), child_no);
+out:
+       logfs_put_write_page(ipage);
+       return ret;
+}
+
+static int logfs_write_rec(struct inode *inode, struct page *page,
+               pgoff_t bix, level_t target_level, long flags)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct write_control wc = {
+               .ofs = li->li_data[INDIRECT_INDEX],
+               .flags = flags,
+       };
+       int ret;
+
+       alloc_inode_block(inode);
+
+       if (li->li_height > (__force u8)target_level)
+               ret = __logfs_write_rec(inode, page, &wc, bix, target_level,
+                               LEVEL(li->li_height));
+       else
+               ret = logfs_write_i0(inode, page, &wc);
+       if (ret)
+               return ret;
+
+       if (li->li_data[INDIRECT_INDEX] != wc.ofs) {
+               li->li_data[INDIRECT_INDEX] = wc.ofs;
+               logfs_set_alias(inode->i_sb, li->li_block,
+                               INDIRECT_INDEX + INODE_POINTER_OFS);
+       }
+       return ret;
+}
+
+void logfs_add_transaction(struct inode *inode, struct logfs_transaction *ta)
+{
+       alloc_inode_block(inode);
+       logfs_inode(inode)->li_block->ta = ta;
+}
+
+void logfs_del_transaction(struct inode *inode, struct logfs_transaction *ta)
+{
+       struct logfs_block *block = logfs_inode(inode)->li_block;
+
+       if (block && block->ta)
+               block->ta = NULL;
+}
+
+static int grow_inode(struct inode *inode, u64 bix, level_t level)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       u8 height = (__force u8)level;
+       struct page *page;
+       struct write_control wc = {
+               .flags = WF_WRITE,
+       };
+       int err;
+
+       BUG_ON(height > 5 || li->li_height > 5);
+       while (height > li->li_height || bix >= maxbix(li->li_height)) {
+               page = logfs_get_write_page(inode, I0_BLOCKS + 1,
+                               LEVEL(li->li_height + 1));
+               if (!page)
+                       return -ENOMEM;
+               logfs_read_empty(page);
+               alloc_indirect_block(inode, page, 1);
+               block_set_pointer(page, 0, li->li_data[INDIRECT_INDEX]);
+               err = logfs_write_i0(inode, page, &wc);
+               logfs_put_write_page(page);
+               if (err)
+                       return err;
+               li->li_data[INDIRECT_INDEX] = wc.ofs;
+               wc.ofs = 0;
+               li->li_height++;
+               logfs_set_alias(inode->i_sb, li->li_block, INODE_HEIGHT_OFS);
+       }
+       return 0;
+}
+
+static int __logfs_write_buf(struct inode *inode, struct page *page, long flags)
+{
+       struct logfs_super *super = logfs_super(inode->i_sb);
+       pgoff_t index = page->index;
+       u64 bix;
+       level_t level;
+       int err;
+
+       flags |= WF_WRITE | WF_DELETE;
+       inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+
+       logfs_unpack_index(index, &bix, &level);
+       if (logfs_block(page) && logfs_block(page)->reserved_bytes)
+               super->s_dirty_pages -= logfs_block(page)->reserved_bytes;
+
+       if (index < I0_BLOCKS)
+               return logfs_write_direct(inode, page, flags);
+
+       bix = adjust_bix(bix, level);
+       err = grow_inode(inode, bix, level);
+       if (err)
+               return err;
+       return logfs_write_rec(inode, page, bix, level, flags);
+}
+
+int logfs_write_buf(struct inode *inode, struct page *page, long flags)
+{
+       struct super_block *sb = inode->i_sb;
+       int ret;
+
+       logfs_get_wblocks(sb, page, flags & WF_LOCK);
+       ret = __logfs_write_buf(inode, page, flags);
+       logfs_put_wblocks(sb, page, flags & WF_LOCK);
+       return ret;
+}
+
+static int __logfs_delete(struct inode *inode, struct page *page)
+{
+       long flags = WF_DELETE;
+
+       inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+
+       if (page->index < I0_BLOCKS)
+               return logfs_write_direct(inode, page, flags);
+       return logfs_write_rec(inode, page, page->index, 0, flags);
+}
+
+int logfs_delete(struct inode *inode, pgoff_t index,
+               struct shadow_tree *shadow_tree)
+{
+       struct super_block *sb = inode->i_sb;
+       struct page *page;
+       int ret;
+
+       page = logfs_get_read_page(inode, index, 0);
+       if (!page)
+               return -ENOMEM;
+
+       logfs_get_wblocks(sb, page, 1);
+       ret = __logfs_delete(inode, page);
+       logfs_put_wblocks(sb, page, 1);
+
+       logfs_put_read_page(page);
+
+       return ret;
+}
+
+/* Rewrite cannot mark the inode dirty but has to write it immediatly. */
+int logfs_rewrite_block(struct inode *inode, u64 bix, u64 ofs,
+               gc_level_t gc_level, long flags)
+{
+       level_t level = shrink_level(gc_level);
+       struct page *page;
+       int err;
+
+       page = logfs_get_write_page(inode, bix, level);
+       if (!page)
+               return -ENOMEM;
+
+       err = logfs_segment_read(inode, page, ofs, bix, level);
+       if (!err) {
+               if (level != 0)
+                       alloc_indirect_block(inode, page, 0);
+               err = logfs_write_buf(inode, page, flags);
+       }
+       logfs_put_write_page(page);
+       return err;
+}
+
+static int truncate_data_block(struct inode *inode, struct page *page,
+               u64 ofs, struct logfs_shadow *shadow, u64 size)
+{
+       loff_t pageofs = page->index << inode->i_sb->s_blocksize_bits;
+       u64 bix;
+       level_t level;
+       int err;
+
+       /* Does truncation happen within this page? */
+       if (size <= pageofs || size - pageofs >= PAGE_SIZE)
+               return 0;
+
+       logfs_unpack_index(page->index, &bix, &level);
+       BUG_ON(level != 0);
+
+       err = logfs_segment_read(inode, page, ofs, bix, level);
+       if (err)
+               return err;
+
+       zero_user_segment(page, size - pageofs, PAGE_CACHE_SIZE);
+       return logfs_segment_write(inode, page, shadow);
+}
+
+static int logfs_truncate_i0(struct inode *inode, struct page *page,
+               struct write_control *wc, u64 size)
+{
+       struct logfs_shadow *shadow;
+       u64 bix;
+       level_t level;
+       int err = 0;
+
+       logfs_unpack_index(page->index, &bix, &level);
+       BUG_ON(level != 0);
+       shadow = alloc_shadow(inode, bix, level, wc->ofs);
+
+       err = truncate_data_block(inode, page, wc->ofs, shadow, size);
+       if (err) {
+               free_shadow(inode, shadow);
+               return err;
+       }
+
+       logfs_segment_delete(inode, shadow);
+       set_iused(inode, shadow);
+       fill_shadow_tree(inode, page, shadow);
+       wc->ofs = shadow->new_ofs;
+       return 0;
+}
+
+static int logfs_truncate_direct(struct inode *inode, u64 size)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct write_control wc;
+       struct page *page;
+       int e;
+       int err;
+
+       alloc_inode_block(inode);
+
+       for (e = I0_BLOCKS - 1; e >= 0; e--) {
+               if (size > (e+1) * LOGFS_BLOCKSIZE)
+                       break;
+
+               wc.ofs = li->li_data[e];
+               if (!wc.ofs)
+                       continue;
+
+               page = logfs_get_write_page(inode, e, 0);
+               if (!page)
+                       return -ENOMEM;
+               err = logfs_segment_read(inode, page, wc.ofs, e, 0);
+               if (err) {
+                       logfs_put_write_page(page);
+                       return err;
+               }
+               err = logfs_truncate_i0(inode, page, &wc, size);
+               logfs_put_write_page(page);
+               if (err)
+                       return err;
+
+               li->li_data[e] = wc.ofs;
+       }
+       return 0;
+}
+
+/* FIXME: these need to become per-sb once we support different blocksizes */
+static u64 __logfs_step[] = {
+       1,
+       I1_BLOCKS,
+       I2_BLOCKS,
+       I3_BLOCKS,
+};
+
+static u64 __logfs_start_index[] = {
+       I0_BLOCKS,
+       I1_BLOCKS,
+       I2_BLOCKS,
+       I3_BLOCKS
+};
+
+static inline u64 logfs_step(level_t level)
+{
+       return __logfs_step[(__force u8)level];
+}
+
+static inline u64 logfs_factor(u8 level)
+{
+       return __logfs_step[level] * LOGFS_BLOCKSIZE;
+}
+
+static inline u64 logfs_start_index(level_t level)
+{
+       return __logfs_start_index[(__force u8)level];
+}
+
+static void logfs_unpack_raw_index(pgoff_t index, u64 *bix, level_t *level)
+{
+       logfs_unpack_index(index, bix, level);
+       if (*bix <= logfs_start_index(SUBLEVEL(*level)))
+               *bix = 0;
+}
+
+static int __logfs_truncate_rec(struct inode *inode, struct page *ipage,
+               struct write_control *this_wc, u64 size)
+{
+       int truncate_happened = 0;
+       int e, err = 0;
+       u64 bix, child_bix, next_bix;
+       level_t level;
+       struct page *page;
+       struct write_control child_wc = { /* FIXME: flags */ };
+
+       logfs_unpack_raw_index(ipage->index, &bix, &level);
+       err = logfs_segment_read(inode, ipage, this_wc->ofs, bix, level);
+       if (err)
+               return err;
+
+       for (e = LOGFS_BLOCK_FACTOR - 1; e >= 0; e--) {
+               child_bix = bix + e * logfs_step(SUBLEVEL(level));
+               next_bix = child_bix + logfs_step(SUBLEVEL(level));
+               if (size > next_bix * LOGFS_BLOCKSIZE)
+                       break;
+
+               child_wc.ofs = pure_ofs(block_get_pointer(ipage, e));
+               if (!child_wc.ofs)
+                       continue;
+
+               page = logfs_get_write_page(inode, child_bix, SUBLEVEL(level));
+               if (!page)
+                       return -ENOMEM;
+
+               if ((__force u8)level > 1)
+                       err = __logfs_truncate_rec(inode, page, &child_wc, size);
+               else
+                       err = logfs_truncate_i0(inode, page, &child_wc, size);
+               logfs_put_write_page(page);
+               if (err)
+                       return err;
+
+               truncate_happened = 1;
+               alloc_indirect_block(inode, ipage, 0);
+               block_set_pointer(ipage, e, child_wc.ofs);
+       }
+
+       if (!truncate_happened) {
+               printk("ineffectual truncate (%lx, %lx, %llx)\n", inode->i_ino, ipage->index, size);
+               return 0;
+       }
+
+       this_wc->flags = WF_DELETE;
+       if (logfs_block(ipage)->partial)
+               this_wc->flags |= WF_WRITE;
+
+       return logfs_write_i0(inode, ipage, this_wc);
+}
+
+static int logfs_truncate_rec(struct inode *inode, u64 size)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct write_control wc = {
+               .ofs = li->li_data[INDIRECT_INDEX],
+       };
+       struct page *page;
+       int err;
+
+       alloc_inode_block(inode);
+
+       if (!wc.ofs)
+               return 0;
+
+       page = logfs_get_write_page(inode, 0, LEVEL(li->li_height));
+       if (!page)
+               return -ENOMEM;
+
+       err = __logfs_truncate_rec(inode, page, &wc, size);
+       logfs_put_write_page(page);
+       if (err)
+               return err;
+
+       if (li->li_data[INDIRECT_INDEX] != wc.ofs)
+               li->li_data[INDIRECT_INDEX] = wc.ofs;
+       return 0;
+}
+
+static int __logfs_truncate(struct inode *inode, u64 size)
+{
+       int ret;
+
+       if (size >= logfs_factor(logfs_inode(inode)->li_height))
+               return 0;
+
+       ret = logfs_truncate_rec(inode, size);
+       if (ret)
+               return ret;
+
+       return logfs_truncate_direct(inode, size);
+}
+
+int logfs_truncate(struct inode *inode, u64 size)
+{
+       struct super_block *sb = inode->i_sb;
+       int err;
+
+       logfs_get_wblocks(sb, NULL, 1);
+       err = __logfs_truncate(inode, size);
+       if (!err)
+               err = __logfs_write_inode(inode, 0);
+       logfs_put_wblocks(sb, NULL, 1);
+
+       if (!err)
+               err = vmtruncate(inode, size);
+
+       /* I don't trust error recovery yet. */
+       WARN_ON(err);
+       return err;
+}
+
+static void move_page_to_inode(struct inode *inode, struct page *page)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct logfs_block *block = logfs_block(page);
+
+       if (!block)
+               return;
+
+       log_blockmove("move_page_to_inode(%llx, %llx, %x)\n",
+                       block->ino, block->bix, block->level);
+       BUG_ON(li->li_block);
+       block->ops = &inode_block_ops;
+       block->inode = inode;
+       li->li_block = block;
+
+       block->page = NULL;
+       page->private = 0;
+       ClearPagePrivate(page);
+}
+
+static void move_inode_to_page(struct page *page, struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+       struct logfs_block *block = li->li_block;
+
+       if (!block)
+               return;
+
+       log_blockmove("move_inode_to_page(%llx, %llx, %x)\n",
+                       block->ino, block->bix, block->level);
+       BUG_ON(PagePrivate(page));
+       block->ops = &indirect_block_ops;
+       block->page = page;
+       page->private = (unsigned long)block;
+       SetPagePrivate(page);
+
+       block->inode = NULL;
+       li->li_block = NULL;
+}
+
+int logfs_read_inode(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *master_inode = super->s_master_inode;
+       struct page *page;
+       struct logfs_disk_inode *di;
+       u64 ino = inode->i_ino;
+
+       if (ino << sb->s_blocksize_bits > i_size_read(master_inode))
+               return -ENODATA;
+       if (!logfs_exist_block(master_inode, ino))
+               return -ENODATA;
+
+       page = read_cache_page(master_inode->i_mapping, ino,
+                       (filler_t *)logfs_readpage, NULL);
+       if (IS_ERR(page))
+               return PTR_ERR(page);
+
+       di = kmap_atomic(page, KM_USER0);
+       logfs_disk_to_inode(di, inode);
+       kunmap_atomic(di, KM_USER0);
+       move_page_to_inode(inode, page);
+       page_cache_release(page);
+       return 0;
+}
+
+/* Caller must logfs_put_write_page(page); */
+static struct page *inode_to_page(struct inode *inode)
+{
+       struct inode *master_inode = logfs_super(inode->i_sb)->s_master_inode;
+       struct logfs_disk_inode *di;
+       struct page *page;
+
+       BUG_ON(inode->i_ino == LOGFS_INO_MASTER);
+
+       page = logfs_get_write_page(master_inode, inode->i_ino, 0);
+       if (!page)
+               return NULL;
+
+       di = kmap_atomic(page, KM_USER0);
+       logfs_inode_to_disk(inode, di);
+       kunmap_atomic(di, KM_USER0);
+       move_inode_to_page(page, inode);
+       return page;
+}
+
+/* Cheaper version of write_inode.  All changes are concealed in
+ * aliases, which are moved back.  No write to the medium happens.
+ */
+void logfs_clear_inode(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       struct logfs_inode *li = logfs_inode(inode);
+       struct logfs_block *block = li->li_block;
+       struct page *page;
+
+       /* Only deleted files may be dirty at this point */
+       BUG_ON(inode->i_state & I_DIRTY && inode->i_nlink);
+       if (!block)
+               return;
+       if ((logfs_super(sb)->s_flags & LOGFS_SB_FLAG_SHUTDOWN)) {
+               block->ops->free_block(inode->i_sb, block);
+               return;
+       }
+
+       BUG_ON(inode->i_ino < LOGFS_RESERVED_INOS);
+       page = inode_to_page(inode);
+       BUG_ON(!page); /* FIXME: Use emergency page */
+       logfs_put_write_page(page);
+}
+
+static int do_write_inode(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       struct inode *master_inode = logfs_super(sb)->s_master_inode;
+       loff_t size = (inode->i_ino + 1) << inode->i_sb->s_blocksize_bits;
+       struct page *page;
+       int err;
+
+       BUG_ON(inode->i_ino == LOGFS_INO_MASTER);
+       /* FIXME: lock inode */
+
+       if (i_size_read(master_inode) < size)
+               i_size_write(master_inode, size);
+
+       /* TODO: Tell vfs this inode is clean now */
+
+       page = inode_to_page(inode);
+       if (!page)
+               return -ENOMEM;
+
+       /* FIXME: transaction is part of logfs_block now.  Is that enough? */
+       err = logfs_write_buf(master_inode, page, 0);
+       logfs_put_write_page(page);
+       return err;
+}
+
+static void logfs_mod_segment_entry(struct super_block *sb, u32 segno,
+               int write,
+               void (*change_se)(struct logfs_segment_entry *, long),
+               long arg)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *inode;
+       struct page *page;
+       struct logfs_segment_entry *se;
+       pgoff_t page_no;
+       int child_no;
+
+       page_no = segno >> (sb->s_blocksize_bits - 3);
+       child_no = segno & ((sb->s_blocksize >> 3) - 1);
+
+       inode = super->s_segfile_inode;
+       page = logfs_get_write_page(inode, page_no, 0);
+       BUG_ON(!page); /* FIXME: We need some reserve page for this case */
+       if (!PageUptodate(page))
+               logfs_read_block(inode, page, WRITE);
+
+       if (write)
+               alloc_indirect_block(inode, page, 0);
+       se = kmap_atomic(page, KM_USER0);
+       change_se(se + child_no, arg);
+       if (write) {
+               logfs_set_alias(sb, logfs_block(page), child_no);
+               BUG_ON((int)be32_to_cpu(se[child_no].valid) > super->s_segsize);
+       }
+       kunmap_atomic(se, KM_USER0);
+
+       logfs_put_write_page(page);
+}
+
+static void __get_segment_entry(struct logfs_segment_entry *se, long _target)
+{
+       struct logfs_segment_entry *target = (void *)_target;
+
+       *target = *se;
+}
+
+void logfs_get_segment_entry(struct super_block *sb, u32 segno,
+               struct logfs_segment_entry *se)
+{
+       logfs_mod_segment_entry(sb, segno, 0, __get_segment_entry, (long)se);
+}
+
+static void __set_segment_used(struct logfs_segment_entry *se, long increment)
+{
+       u32 valid;
+
+       valid = be32_to_cpu(se->valid);
+       valid += increment;
+       se->valid = cpu_to_be32(valid);
+}
+
+void logfs_set_segment_used(struct super_block *sb, u64 ofs, int increment)
+{
+       struct logfs_super *super = logfs_super(sb);
+       u32 segno = ofs >> super->s_segshift;
+
+       if (!increment)
+               return;
+
+       logfs_mod_segment_entry(sb, segno, 1, __set_segment_used, increment);
+}
+
+static void __set_segment_erased(struct logfs_segment_entry *se, long ec_level)
+{
+       se->ec_level = cpu_to_be32(ec_level);
+}
+
+void logfs_set_segment_erased(struct super_block *sb, u32 segno, u32 ec,
+               gc_level_t gc_level)
+{
+       u32 ec_level = ec << 4 | (__force u8)gc_level;
+
+       logfs_mod_segment_entry(sb, segno, 1, __set_segment_erased, ec_level);
+}
+
+static void __set_segment_reserved(struct logfs_segment_entry *se, long ignore)
+{
+       se->valid = cpu_to_be32(RESERVED);
+}
+
+void logfs_set_segment_reserved(struct super_block *sb, u32 segno)
+{
+       logfs_mod_segment_entry(sb, segno, 1, __set_segment_reserved, 0);
+}
+
+static void __set_segment_unreserved(struct logfs_segment_entry *se,
+               long ec_level)
+{
+       se->valid = 0;
+       se->ec_level = cpu_to_be32(ec_level);
+}
+
+void logfs_set_segment_unreserved(struct super_block *sb, u32 segno, u32 ec)
+{
+       u32 ec_level = ec << 4;
+
+       logfs_mod_segment_entry(sb, segno, 1, __set_segment_unreserved,
+                       ec_level);
+}
+
+int __logfs_write_inode(struct inode *inode, long flags)
+{
+       struct super_block *sb = inode->i_sb;
+       int ret;
+
+       logfs_get_wblocks(sb, NULL, flags & WF_LOCK);
+       ret = do_write_inode(inode);
+       logfs_put_wblocks(sb, NULL, flags & WF_LOCK);
+       return ret;
+}
+
+static int do_delete_inode(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       struct inode *master_inode = logfs_super(sb)->s_master_inode;
+       struct page *page;
+       int ret;
+
+       page = logfs_get_write_page(master_inode, inode->i_ino, 0);
+       if (!page)
+               return -ENOMEM;
+
+       move_inode_to_page(page, inode);
+
+       logfs_get_wblocks(sb, page, 1);
+       ret = __logfs_delete(master_inode, page);
+       logfs_put_wblocks(sb, page, 1);
+
+       logfs_put_write_page(page);
+       return ret;
+}
+
+/*
+ * ZOMBIE inodes have already been deleted before and should remain dead,
+ * if it weren't for valid checking.  No need to kill them again here.
+ */
+void logfs_delete_inode(struct inode *inode)
+{
+       struct logfs_inode *li = logfs_inode(inode);
+
+       if (!(li->li_flags & LOGFS_IF_ZOMBIE)) {
+               li->li_flags |= LOGFS_IF_ZOMBIE;
+               if (i_size_read(inode) > 0)
+                       logfs_truncate(inode, 0);
+               do_delete_inode(inode);
+       }
+       truncate_inode_pages(&inode->i_data, 0);
+       clear_inode(inode);
+}
+
+void btree_write_block(struct logfs_block *block)
+{
+       struct inode *inode;
+       struct page *page;
+       int err, cookie;
+
+       inode = logfs_safe_iget(block->sb, block->ino, &cookie);
+       page = logfs_get_write_page(inode, block->bix, block->level);
+
+       err = logfs_readpage_nolock(page);
+       BUG_ON(err);
+       BUG_ON(!PagePrivate(page));
+       BUG_ON(logfs_block(page) != block);
+       err = __logfs_write_buf(inode, page, 0);
+       BUG_ON(err);
+       BUG_ON(PagePrivate(page) || page->private);
+
+       logfs_put_write_page(page);
+       logfs_safe_iput(inode, cookie);
+}
+
+/**
+ * logfs_inode_write - write inode or dentry objects
+ *
+ * @inode:             parent inode (ifile or directory)
+ * @buf:               object to write (inode or dentry)
+ * @n:                 object size
+ * @_pos:              object number (file position in blocks/objects)
+ * @flags:             write flags
+ * @lock:              0 if write lock is already taken, 1 otherwise
+ * @shadow_tree:       shadow below this inode
+ *
+ * FIXME: All caller of this put a 200-300 byte variable on the stack,
+ * only to call here and do a memcpy from that stack variable.  A good
+ * example of wasted performance and stack space.
+ */
+int logfs_inode_write(struct inode *inode, const void *buf, size_t count,
+               loff_t bix, long flags, struct shadow_tree *shadow_tree)
+{
+       loff_t pos = bix << inode->i_sb->s_blocksize_bits;
+       int err;
+       struct page *page;
+       void *pagebuf;
+
+       BUG_ON(pos & (LOGFS_BLOCKSIZE-1));
+       BUG_ON(count > LOGFS_BLOCKSIZE);
+       page = logfs_get_write_page(inode, bix, 0);
+       if (!page)
+               return -ENOMEM;
+
+       pagebuf = kmap_atomic(page, KM_USER0);
+       memcpy(pagebuf, buf, count);
+       flush_dcache_page(page);
+       kunmap_atomic(pagebuf, KM_USER0);
+
+       if (i_size_read(inode) < pos + LOGFS_BLOCKSIZE)
+               i_size_write(inode, pos + LOGFS_BLOCKSIZE);
+
+       err = logfs_write_buf(inode, page, flags);
+       logfs_put_write_page(page);
+       return err;
+}
+
+int logfs_open_segfile(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *inode;
+
+       inode = logfs_read_meta_inode(sb, LOGFS_INO_SEGFILE);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+       super->s_segfile_inode = inode;
+       return 0;
+}
+
+int logfs_init_rw(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int min_fill = 3 * super->s_no_blocks;
+
+       INIT_LIST_HEAD(&super->s_object_alias);
+       mutex_init(&super->s_write_mutex);
+       super->s_block_pool = mempool_create_kmalloc_pool(min_fill,
+                       sizeof(struct logfs_block));
+       super->s_shadow_pool = mempool_create_kmalloc_pool(min_fill,
+                       sizeof(struct logfs_shadow));
+       return 0;
+}
+
+void logfs_cleanup_rw(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       destroy_meta_inode(super->s_segfile_inode);
+       if (super->s_block_pool)
+               mempool_destroy(super->s_block_pool);
+       if (super->s_shadow_pool)
+               mempool_destroy(super->s_shadow_pool);
+}
diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c
new file mode 100644 (file)
index 0000000..1a14f99
--- /dev/null
@@ -0,0 +1,927 @@
+/*
+ * fs/logfs/segment.c  - Handling the Object Store
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ *
+ * Object store or ostore makes up the complete device with exception of
+ * the superblock and journal areas.  Apart from its own metadata it stores
+ * three kinds of objects: inodes, dentries and blocks, both data and indirect.
+ */
+#include "logfs.h"
+
+static int logfs_mark_segment_bad(struct super_block *sb, u32 segno)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct btree_head32 *head = &super->s_reserved_segments;
+       int err;
+
+       err = btree_insert32(head, segno, (void *)1, GFP_NOFS);
+       if (err)
+               return err;
+       logfs_super(sb)->s_bad_segments++;
+       /* FIXME: write to journal */
+       return 0;
+}
+
+int logfs_erase_segment(struct super_block *sb, u32 segno, int ensure_erase)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       super->s_gec++;
+
+       return super->s_devops->erase(sb, (u64)segno << super->s_segshift,
+                       super->s_segsize, ensure_erase);
+}
+
+static s64 logfs_get_free_bytes(struct logfs_area *area, size_t bytes)
+{
+       s32 ofs;
+
+       logfs_open_area(area, bytes);
+
+       ofs = area->a_used_bytes;
+       area->a_used_bytes += bytes;
+       BUG_ON(area->a_used_bytes >= logfs_super(area->a_sb)->s_segsize);
+
+       return dev_ofs(area->a_sb, area->a_segno, ofs);
+}
+
+static struct page *get_mapping_page(struct super_block *sb, pgoff_t index,
+               int use_filler)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       filler_t *filler = super->s_devops->readpage;
+       struct page *page;
+
+       BUG_ON(mapping_gfp_mask(mapping) & __GFP_FS);
+       if (use_filler)
+               page = read_cache_page(mapping, index, filler, sb);
+       else {
+               page = find_or_create_page(mapping, index, GFP_NOFS);
+               unlock_page(page);
+       }
+       return page;
+}
+
+void __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
+               int use_filler)
+{
+       pgoff_t index = ofs >> PAGE_SHIFT;
+       struct page *page;
+       long offset = ofs & (PAGE_SIZE-1);
+       long copylen;
+
+       /* Only logfs_wbuf_recover may use len==0 */
+       BUG_ON(!len && !use_filler);
+       do {
+               copylen = min((ulong)len, PAGE_SIZE - offset);
+
+               page = get_mapping_page(area->a_sb, index, use_filler);
+               SetPageUptodate(page);
+               BUG_ON(!page); /* FIXME: reserve a pool */
+               memcpy(page_address(page) + offset, buf, copylen);
+               SetPagePrivate(page);
+               page_cache_release(page);
+
+               buf += copylen;
+               len -= copylen;
+               offset = 0;
+               index++;
+       } while (len);
+}
+
+/*
+ * bdev_writeseg will write full pages.  Memset the tail to prevent data leaks.
+ */
+static void pad_wbuf(struct logfs_area *area, int final)
+{
+       struct super_block *sb = area->a_sb;
+       struct logfs_super *super = logfs_super(sb);
+       struct page *page;
+       u64 ofs = dev_ofs(sb, area->a_segno, area->a_used_bytes);
+       pgoff_t index = ofs >> PAGE_SHIFT;
+       long offset = ofs & (PAGE_SIZE-1);
+       u32 len = PAGE_SIZE - offset;
+
+       if (len == PAGE_SIZE) {
+               /* The math in this function can surely use some love */
+               len = 0;
+       }
+       if (len) {
+               BUG_ON(area->a_used_bytes >= super->s_segsize);
+
+               page = get_mapping_page(area->a_sb, index, 0);
+               BUG_ON(!page); /* FIXME: reserve a pool */
+               memset(page_address(page) + offset, 0xff, len);
+               SetPagePrivate(page);
+               page_cache_release(page);
+       }
+
+       if (!final)
+               return;
+
+       area->a_used_bytes += len;
+       for ( ; area->a_used_bytes < super->s_segsize;
+                       area->a_used_bytes += PAGE_SIZE) {
+               /* Memset another page */
+               index++;
+               page = get_mapping_page(area->a_sb, index, 0);
+               BUG_ON(!page); /* FIXME: reserve a pool */
+               memset(page_address(page), 0xff, PAGE_SIZE);
+               SetPagePrivate(page);
+               page_cache_release(page);
+       }
+}
+
+/*
+ * We have to be careful with the alias tree.  Since lookup is done by bix,
+ * it needs to be normalized, so 14, 15, 16, etc. all match when dealing with
+ * indirect blocks.  So always use it through accessor functions.
+ */
+static void *alias_tree_lookup(struct super_block *sb, u64 ino, u64 bix,
+               level_t level)
+{
+       struct btree_head128 *head = &logfs_super(sb)->s_object_alias_tree;
+       pgoff_t index = logfs_pack_index(bix, level);
+
+       return btree_lookup128(head, ino, index);
+}
+
+static int alias_tree_insert(struct super_block *sb, u64 ino, u64 bix,
+               level_t level, void *val)
+{
+       struct btree_head128 *head = &logfs_super(sb)->s_object_alias_tree;
+       pgoff_t index = logfs_pack_index(bix, level);
+
+       return btree_insert128(head, ino, index, val, GFP_NOFS);
+}
+
+static int btree_write_alias(struct super_block *sb, struct logfs_block *block,
+               write_alias_t *write_one_alias)
+{
+       struct object_alias_item *item;
+       int err;
+
+       list_for_each_entry(item, &block->item_list, list) {
+               err = write_alias_journal(sb, block->ino, block->bix,
+                               block->level, item->child_no, item->val);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static gc_level_t btree_block_level(struct logfs_block *block)
+{
+       return expand_level(block->ino, block->level);
+}
+
+static struct logfs_block_ops btree_block_ops = {
+       .write_block    = btree_write_block,
+       .block_level    = btree_block_level,
+       .free_block     = __free_block,
+       .write_alias    = btree_write_alias,
+};
+
+int logfs_load_object_aliases(struct super_block *sb,
+               struct logfs_obj_alias *oa, int count)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_block *block;
+       struct object_alias_item *item;
+       u64 ino, bix;
+       level_t level;
+       int i, err;
+
+       super->s_flags |= LOGFS_SB_FLAG_OBJ_ALIAS;
+       count /= sizeof(*oa);
+       for (i = 0; i < count; i++) {
+               item = mempool_alloc(super->s_alias_pool, GFP_NOFS);
+               if (!item)
+                       return -ENOMEM;
+               memset(item, 0, sizeof(*item));
+
+               super->s_no_object_aliases++;
+               item->val = oa[i].val;
+               item->child_no = be16_to_cpu(oa[i].child_no);
+
+               ino = be64_to_cpu(oa[i].ino);
+               bix = be64_to_cpu(oa[i].bix);
+               level = LEVEL(oa[i].level);
+
+               log_aliases("logfs_load_object_aliases(%llx, %llx, %x, %x) %llx\n",
+                               ino, bix, level, item->child_no,
+                               be64_to_cpu(item->val));
+               block = alias_tree_lookup(sb, ino, bix, level);
+               if (!block) {
+                       block = __alloc_block(sb, ino, bix, level);
+                       block->ops = &btree_block_ops;
+                       err = alias_tree_insert(sb, ino, bix, level, block);
+                       BUG_ON(err); /* mempool empty */
+               }
+               if (test_and_set_bit(item->child_no, block->alias_map)) {
+                       printk(KERN_ERR"LogFS: Alias collision detected\n");
+                       return -EIO;
+               }
+               list_move_tail(&block->alias_list, &super->s_object_alias);
+               list_add(&item->list, &block->item_list);
+       }
+       return 0;
+}
+
+static void kill_alias(void *_block, unsigned long ignore0,
+               u64 ignore1, u64 ignore2, size_t ignore3)
+{
+       struct logfs_block *block = _block;
+       struct super_block *sb = block->sb;
+       struct logfs_super *super = logfs_super(sb);
+       struct object_alias_item *item;
+
+       while (!list_empty(&block->item_list)) {
+               item = list_entry(block->item_list.next, typeof(*item), list);
+               list_del(&item->list);
+               mempool_free(item, super->s_alias_pool);
+       }
+       block->ops->free_block(sb, block);
+}
+
+static int obj_type(struct inode *inode, level_t level)
+{
+       if (level == 0) {
+               if (S_ISDIR(inode->i_mode))
+                       return OBJ_DENTRY;
+               if (inode->i_ino == LOGFS_INO_MASTER)
+                       return OBJ_INODE;
+       }
+       return OBJ_BLOCK;
+}
+
+static int obj_len(struct super_block *sb, int obj_type)
+{
+       switch (obj_type) {
+       case OBJ_DENTRY:
+               return sizeof(struct logfs_disk_dentry);
+       case OBJ_INODE:
+               return sizeof(struct logfs_disk_inode);
+       case OBJ_BLOCK:
+               return sb->s_blocksize;
+       default:
+               BUG();
+       }
+}
+
+static int __logfs_segment_write(struct inode *inode, void *buf,
+               struct logfs_shadow *shadow, int type, int len, int compr)
+{
+       struct logfs_area *area;
+       struct super_block *sb = inode->i_sb;
+       s64 ofs;
+       struct logfs_object_header h;
+       int acc_len;
+
+       if (shadow->gc_level == 0)
+               acc_len = len;
+       else
+               acc_len = obj_len(sb, type);
+
+       area = get_area(sb, shadow->gc_level);
+       ofs = logfs_get_free_bytes(area, len + LOGFS_OBJECT_HEADERSIZE);
+       LOGFS_BUG_ON(ofs <= 0, sb);
+       /*
+        * Order is important.  logfs_get_free_bytes(), by modifying the
+        * segment file, may modify the content of the very page we're about
+        * to write now.  Which is fine, as long as the calculated crc and
+        * written data still match.  So do the modifications _before_
+        * calculating the crc.
+        */
+
+       h.len   = cpu_to_be16(len);
+       h.type  = type;
+       h.compr = compr;
+       h.ino   = cpu_to_be64(inode->i_ino);
+       h.bix   = cpu_to_be64(shadow->bix);
+       h.crc   = logfs_crc32(&h, sizeof(h) - 4, 4);
+       h.data_crc = logfs_crc32(buf, len, 0);
+
+       logfs_buf_write(area, ofs, &h, sizeof(h));
+       logfs_buf_write(area, ofs + LOGFS_OBJECT_HEADERSIZE, buf, len);
+
+       shadow->new_ofs = ofs;
+       shadow->new_len = acc_len + LOGFS_OBJECT_HEADERSIZE;
+
+       return 0;
+}
+
+static s64 logfs_segment_write_compress(struct inode *inode, void *buf,
+               struct logfs_shadow *shadow, int type, int len)
+{
+       struct super_block *sb = inode->i_sb;
+       void *compressor_buf = logfs_super(sb)->s_compressed_je;
+       ssize_t compr_len;
+       int ret;
+
+       mutex_lock(&logfs_super(sb)->s_journal_mutex);
+       compr_len = logfs_compress(buf, compressor_buf, len, len);
+
+       if (compr_len >= 0) {
+               ret = __logfs_segment_write(inode, compressor_buf, shadow,
+                               type, compr_len, COMPR_ZLIB);
+       } else {
+               ret = __logfs_segment_write(inode, buf, shadow, type, len,
+                               COMPR_NONE);
+       }
+       mutex_unlock(&logfs_super(sb)->s_journal_mutex);
+       return ret;
+}
+
+/**
+ * logfs_segment_write - write data block to object store
+ * @inode:             inode containing data
+ *
+ * Returns an errno or zero.
+ */
+int logfs_segment_write(struct inode *inode, struct page *page,
+               struct logfs_shadow *shadow)
+{
+       struct super_block *sb = inode->i_sb;
+       struct logfs_super *super = logfs_super(sb);
+       int do_compress, type, len;
+       int ret;
+       void *buf;
+
+       super->s_flags |= LOGFS_SB_FLAG_DIRTY;
+       BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN);
+       do_compress = logfs_inode(inode)->li_flags & LOGFS_IF_COMPRESSED;
+       if (shadow->gc_level != 0) {
+               /* temporarily disable compression for indirect blocks */
+               do_compress = 0;
+       }
+
+       type = obj_type(inode, shrink_level(shadow->gc_level));
+       len = obj_len(sb, type);
+       buf = kmap(page);
+       if (do_compress)
+               ret = logfs_segment_write_compress(inode, buf, shadow, type,
+                               len);
+       else
+               ret = __logfs_segment_write(inode, buf, shadow, type, len,
+                               COMPR_NONE);
+       kunmap(page);
+
+       log_segment("logfs_segment_write(%llx, %llx, %x) %llx->%llx %x->%x\n",
+                       shadow->ino, shadow->bix, shadow->gc_level,
+                       shadow->old_ofs, shadow->new_ofs,
+                       shadow->old_len, shadow->new_len);
+       /* this BUG_ON did catch a locking bug.  useful */
+       BUG_ON(!(shadow->new_ofs & (super->s_segsize - 1)));
+       return ret;
+}
+
+int wbuf_read(struct super_block *sb, u64 ofs, size_t len, void *buf)
+{
+       pgoff_t index = ofs >> PAGE_SHIFT;
+       struct page *page;
+       long offset = ofs & (PAGE_SIZE-1);
+       long copylen;
+
+       while (len) {
+               copylen = min((ulong)len, PAGE_SIZE - offset);
+
+               page = get_mapping_page(sb, index, 1);
+               if (IS_ERR(page))
+                       return PTR_ERR(page);
+               memcpy(buf, page_address(page) + offset, copylen);
+               page_cache_release(page);
+
+               buf += copylen;
+               len -= copylen;
+               offset = 0;
+               index++;
+       }
+       return 0;
+}
+
+/*
+ * The "position" of indirect blocks is ambiguous.  It can be the position
+ * of any data block somewhere behind this indirect block.  So we need to
+ * normalize the positions through logfs_block_mask() before comparing.
+ */
+static int check_pos(struct super_block *sb, u64 pos1, u64 pos2, level_t level)
+{
+       return  (pos1 & logfs_block_mask(sb, level)) !=
+               (pos2 & logfs_block_mask(sb, level));
+}
+
+#if 0
+static int read_seg_header(struct super_block *sb, u64 ofs,
+               struct logfs_segment_header *sh)
+{
+       __be32 crc;
+       int err;
+
+       err = wbuf_read(sb, ofs, sizeof(*sh), sh);
+       if (err)
+               return err;
+       crc = logfs_crc32(sh, sizeof(*sh), 4);
+       if (crc != sh->crc) {
+               printk(KERN_ERR"LOGFS: header crc error at %llx: expected %x, "
+                               "got %x\n", ofs, be32_to_cpu(sh->crc),
+                               be32_to_cpu(crc));
+               return -EIO;
+       }
+       return 0;
+}
+#endif
+
+static int read_obj_header(struct super_block *sb, u64 ofs,
+               struct logfs_object_header *oh)
+{
+       __be32 crc;
+       int err;
+
+       err = wbuf_read(sb, ofs, sizeof(*oh), oh);
+       if (err)
+               return err;
+       crc = logfs_crc32(oh, sizeof(*oh) - 4, 4);
+       if (crc != oh->crc) {
+               printk(KERN_ERR"LOGFS: header crc error at %llx: expected %x, "
+                               "got %x\n", ofs, be32_to_cpu(oh->crc),
+                               be32_to_cpu(crc));
+               return -EIO;
+       }
+       return 0;
+}
+
+static void move_btree_to_page(struct inode *inode, struct page *page,
+               __be64 *data)
+{
+       struct super_block *sb = inode->i_sb;
+       struct logfs_super *super = logfs_super(sb);
+       struct btree_head128 *head = &super->s_object_alias_tree;
+       struct logfs_block *block;
+       struct object_alias_item *item, *next;
+
+       if (!(super->s_flags & LOGFS_SB_FLAG_OBJ_ALIAS))
+               return;
+
+       block = btree_remove128(head, inode->i_ino, page->index);
+       if (!block)
+               return;
+
+       log_blockmove("move_btree_to_page(%llx, %llx, %x)\n",
+                       block->ino, block->bix, block->level);
+       list_for_each_entry_safe(item, next, &block->item_list, list) {
+               data[item->child_no] = item->val;
+               list_del(&item->list);
+               mempool_free(item, super->s_alias_pool);
+       }
+       block->page = page;
+       SetPagePrivate(page);
+       page->private = (unsigned long)block;
+       block->ops = &indirect_block_ops;
+       initialize_block_counters(page, block, data, 0);
+}
+
+/*
+ * This silences a false, yet annoying gcc warning.  I hate it when my editor
+ * jumps into bitops.h each time I recompile this file.
+ * TODO: Complain to gcc folks about this and upgrade compiler.
+ */
+static unsigned long fnb(const unsigned long *addr,
+               unsigned long size, unsigned long offset)
+{
+       return find_next_bit(addr, size, offset);
+}
+
+void move_page_to_btree(struct page *page)
+{
+       struct logfs_block *block = logfs_block(page);
+       struct super_block *sb = block->sb;
+       struct logfs_super *super = logfs_super(sb);
+       struct object_alias_item *item;
+       unsigned long pos;
+       __be64 *child;
+       int err;
+
+       if (super->s_flags & LOGFS_SB_FLAG_SHUTDOWN) {
+               block->ops->free_block(sb, block);
+               return;
+       }
+       log_blockmove("move_page_to_btree(%llx, %llx, %x)\n",
+                       block->ino, block->bix, block->level);
+       super->s_flags |= LOGFS_SB_FLAG_OBJ_ALIAS;
+
+       for (pos = 0; ; pos++) {
+               pos = fnb(block->alias_map, LOGFS_BLOCK_FACTOR, pos);
+               if (pos >= LOGFS_BLOCK_FACTOR)
+                       break;
+
+               item = mempool_alloc(super->s_alias_pool, GFP_NOFS);
+               BUG_ON(!item); /* mempool empty */
+               memset(item, 0, sizeof(*item));
+
+               child = kmap_atomic(page, KM_USER0);
+               item->val = child[pos];
+               kunmap_atomic(child, KM_USER0);
+               item->child_no = pos;
+               list_add(&item->list, &block->item_list);
+       }
+       block->page = NULL;
+       ClearPagePrivate(page);
+       page->private = 0;
+       block->ops = &btree_block_ops;
+       err = alias_tree_insert(block->sb, block->ino, block->bix, block->level,
+                       block);
+       BUG_ON(err); /* mempool empty */
+       ClearPageUptodate(page);
+}
+
+static int __logfs_segment_read(struct inode *inode, void *buf,
+               u64 ofs, u64 bix, level_t level)
+{
+       struct super_block *sb = inode->i_sb;
+       void *compressor_buf = logfs_super(sb)->s_compressed_je;
+       struct logfs_object_header oh;
+       __be32 crc;
+       u16 len;
+       int err, block_len;
+
+       block_len = obj_len(sb, obj_type(inode, level));
+       err = read_obj_header(sb, ofs, &oh);
+       if (err)
+               goto out_err;
+
+       err = -EIO;
+       if (be64_to_cpu(oh.ino) != inode->i_ino
+                       || check_pos(sb, be64_to_cpu(oh.bix), bix, level)) {
+               printk(KERN_ERR"LOGFS: (ino, bix) don't match at %llx: "
+                               "expected (%lx, %llx), got (%llx, %llx)\n",
+                               ofs, inode->i_ino, bix,
+                               be64_to_cpu(oh.ino), be64_to_cpu(oh.bix));
+               goto out_err;
+       }
+
+       len = be16_to_cpu(oh.len);
+
+       switch (oh.compr) {
+       case COMPR_NONE:
+               err = wbuf_read(sb, ofs + LOGFS_OBJECT_HEADERSIZE, len, buf);
+               if (err)
+                       goto out_err;
+               crc = logfs_crc32(buf, len, 0);
+               if (crc != oh.data_crc) {
+                       printk(KERN_ERR"LOGFS: uncompressed data crc error at "
+                                       "%llx: expected %x, got %x\n", ofs,
+                                       be32_to_cpu(oh.data_crc),
+                                       be32_to_cpu(crc));
+                       goto out_err;
+               }
+               break;
+       case COMPR_ZLIB:
+               mutex_lock(&logfs_super(sb)->s_journal_mutex);
+               err = wbuf_read(sb, ofs + LOGFS_OBJECT_HEADERSIZE, len,
+                               compressor_buf);
+               if (err) {
+                       mutex_unlock(&logfs_super(sb)->s_journal_mutex);
+                       goto out_err;
+               }
+               crc = logfs_crc32(compressor_buf, len, 0);
+               if (crc != oh.data_crc) {
+                       printk(KERN_ERR"LOGFS: compressed data crc error at "
+                                       "%llx: expected %x, got %x\n", ofs,
+                                       be32_to_cpu(oh.data_crc),
+                                       be32_to_cpu(crc));
+                       mutex_unlock(&logfs_super(sb)->s_journal_mutex);
+                       goto out_err;
+               }
+               err = logfs_uncompress(compressor_buf, buf, len, block_len);
+               mutex_unlock(&logfs_super(sb)->s_journal_mutex);
+               if (err) {
+                       printk(KERN_ERR"LOGFS: uncompress error at %llx\n", ofs);
+                       goto out_err;
+               }
+               break;
+       default:
+               LOGFS_BUG(sb);
+               err = -EIO;
+               goto out_err;
+       }
+       return 0;
+
+out_err:
+       logfs_set_ro(sb);
+       printk(KERN_ERR"LOGFS: device is read-only now\n");
+       LOGFS_BUG(sb);
+       return err;
+}
+
+/**
+ * logfs_segment_read - read data block from object store
+ * @inode:             inode containing data
+ * @buf:               data buffer
+ * @ofs:               physical data offset
+ * @bix:               block index
+ * @level:             block level
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int logfs_segment_read(struct inode *inode, struct page *page,
+               u64 ofs, u64 bix, level_t level)
+{
+       int err;
+       void *buf;
+
+       if (PageUptodate(page))
+               return 0;
+
+       ofs &= ~LOGFS_FULLY_POPULATED;
+
+       buf = kmap(page);
+       err = __logfs_segment_read(inode, buf, ofs, bix, level);
+       if (!err) {
+               move_btree_to_page(inode, page, buf);
+               SetPageUptodate(page);
+       }
+       kunmap(page);
+       log_segment("logfs_segment_read(%lx, %llx, %x) %llx (%d)\n",
+                       inode->i_ino, bix, level, ofs, err);
+       return err;
+}
+
+int logfs_segment_delete(struct inode *inode, struct logfs_shadow *shadow)
+{
+       struct super_block *sb = inode->i_sb;
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_object_header h;
+       u16 len;
+       int err;
+
+       super->s_flags |= LOGFS_SB_FLAG_DIRTY;
+       BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN);
+       BUG_ON(shadow->old_ofs & LOGFS_FULLY_POPULATED);
+       if (!shadow->old_ofs)
+               return 0;
+
+       log_segment("logfs_segment_delete(%llx, %llx, %x) %llx->%llx %x->%x\n",
+                       shadow->ino, shadow->bix, shadow->gc_level,
+                       shadow->old_ofs, shadow->new_ofs,
+                       shadow->old_len, shadow->new_len);
+       err = read_obj_header(sb, shadow->old_ofs, &h);
+       LOGFS_BUG_ON(err, sb);
+       LOGFS_BUG_ON(be64_to_cpu(h.ino) != inode->i_ino, sb);
+       LOGFS_BUG_ON(check_pos(sb, shadow->bix, be64_to_cpu(h.bix),
+                               shrink_level(shadow->gc_level)), sb);
+
+       if (shadow->gc_level == 0)
+               len = be16_to_cpu(h.len);
+       else
+               len = obj_len(sb, h.type);
+       shadow->old_len = len + sizeof(h);
+       return 0;
+}
+
+static void freeseg(struct super_block *sb, u32 segno)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping = super->s_mapping_inode->i_mapping;
+       struct page *page;
+       u64 ofs, start, end;
+
+       start = dev_ofs(sb, segno, 0);
+       end = dev_ofs(sb, segno + 1, 0);
+       for (ofs = start; ofs < end; ofs += PAGE_SIZE) {
+               page = find_get_page(mapping, ofs >> PAGE_SHIFT);
+               if (!page)
+                       continue;
+               ClearPagePrivate(page);
+               page_cache_release(page);
+       }
+}
+
+int logfs_open_area(struct logfs_area *area, size_t bytes)
+{
+       struct super_block *sb = area->a_sb;
+       struct logfs_super *super = logfs_super(sb);
+       int err, closed = 0;
+
+       if (area->a_is_open && area->a_used_bytes + bytes <= super->s_segsize)
+               return 0;
+
+       if (area->a_is_open) {
+               u64 ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
+               u32 len = super->s_segsize - area->a_written_bytes;
+
+               log_gc("logfs_close_area(%x)\n", area->a_segno);
+               pad_wbuf(area, 1);
+               super->s_devops->writeseg(area->a_sb, ofs, len);
+               freeseg(sb, area->a_segno);
+               closed = 1;
+       }
+
+       area->a_used_bytes = 0;
+       area->a_written_bytes = 0;
+again:
+       area->a_ops->get_free_segment(area);
+       area->a_ops->get_erase_count(area);
+
+       log_gc("logfs_open_area(%x, %x)\n", area->a_segno, area->a_level);
+       err = area->a_ops->erase_segment(area);
+       if (err) {
+               printk(KERN_WARNING "LogFS: Error erasing segment %x\n",
+                               area->a_segno);
+               logfs_mark_segment_bad(sb, area->a_segno);
+               goto again;
+       }
+       area->a_is_open = 1;
+       return closed;
+}
+
+void logfs_sync_area(struct logfs_area *area)
+{
+       struct super_block *sb = area->a_sb;
+       struct logfs_super *super = logfs_super(sb);
+       u64 ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes);
+       u32 len = (area->a_used_bytes - area->a_written_bytes);
+
+       if (super->s_writesize)
+               len &= ~(super->s_writesize - 1);
+       if (len == 0)
+               return;
+       pad_wbuf(area, 0);
+       super->s_devops->writeseg(sb, ofs, len);
+       area->a_written_bytes += len;
+}
+
+void logfs_sync_segments(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i;
+
+       for_each_area(i)
+               logfs_sync_area(super->s_area[i]);
+}
+
+/*
+ * Pick a free segment to be used for this area.  Effectively takes a
+ * candidate from the free list (not really a candidate anymore).
+ */
+static void ostore_get_free_segment(struct logfs_area *area)
+{
+       struct super_block *sb = area->a_sb;
+       struct logfs_super *super = logfs_super(sb);
+
+       if (super->s_free_list.count == 0) {
+               printk(KERN_ERR"LOGFS: ran out of free segments\n");
+               LOGFS_BUG(sb);
+       }
+
+       area->a_segno = get_best_cand(sb, &super->s_free_list, NULL);
+}
+
+static void ostore_get_erase_count(struct logfs_area *area)
+{
+       struct logfs_segment_entry se;
+       u32 ec_level;
+
+       logfs_get_segment_entry(area->a_sb, area->a_segno, &se);
+       BUG_ON(se.ec_level == cpu_to_be32(BADSEG) ||
+                       se.valid == cpu_to_be32(RESERVED));
+
+       ec_level = be32_to_cpu(se.ec_level);
+       area->a_erase_count = (ec_level >> 4) + 1;
+}
+
+static int ostore_erase_segment(struct logfs_area *area)
+{
+       struct super_block *sb = area->a_sb;
+       struct logfs_segment_header sh;
+       u64 ofs;
+       int err;
+
+       err = logfs_erase_segment(sb, area->a_segno, 0);
+       if (err)
+               return err;
+
+       sh.pad = 0;
+       sh.type = SEG_OSTORE;
+       sh.level = (__force u8)area->a_level;
+       sh.segno = cpu_to_be32(area->a_segno);
+       sh.ec = cpu_to_be32(area->a_erase_count);
+       sh.gec = cpu_to_be64(logfs_super(sb)->s_gec);
+       sh.crc = logfs_crc32(&sh, sizeof(sh), 4);
+
+       logfs_set_segment_erased(sb, area->a_segno, area->a_erase_count,
+                       area->a_level);
+
+       ofs = dev_ofs(sb, area->a_segno, 0);
+       area->a_used_bytes = sizeof(sh);
+       logfs_buf_write(area, ofs, &sh, sizeof(sh));
+       return 0;
+}
+
+static const struct logfs_area_ops ostore_area_ops = {
+       .get_free_segment       = ostore_get_free_segment,
+       .get_erase_count        = ostore_get_erase_count,
+       .erase_segment          = ostore_erase_segment,
+};
+
+static void free_area(struct logfs_area *area)
+{
+       if (area)
+               freeseg(area->a_sb, area->a_segno);
+       kfree(area);
+}
+
+static struct logfs_area *alloc_area(struct super_block *sb)
+{
+       struct logfs_area *area;
+
+       area = kzalloc(sizeof(*area), GFP_KERNEL);
+       if (!area)
+               return NULL;
+
+       area->a_sb = sb;
+       return area;
+}
+
+static void map_invalidatepage(struct page *page, unsigned long l)
+{
+       BUG();
+}
+
+static int map_releasepage(struct page *page, gfp_t g)
+{
+       /* Don't release these pages */
+       return 0;
+}
+
+static const struct address_space_operations mapping_aops = {
+       .invalidatepage = map_invalidatepage,
+       .releasepage    = map_releasepage,
+       .set_page_dirty = __set_page_dirty_nobuffers,
+};
+
+int logfs_init_mapping(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct address_space *mapping;
+       struct inode *inode;
+
+       inode = logfs_new_meta_inode(sb, LOGFS_INO_MAPPING);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+       super->s_mapping_inode = inode;
+       mapping = inode->i_mapping;
+       mapping->a_ops = &mapping_aops;
+       /* Would it be possible to use __GFP_HIGHMEM as well? */
+       mapping_set_gfp_mask(mapping, GFP_NOFS);
+       return 0;
+}
+
+int logfs_init_areas(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i = -1;
+
+       super->s_alias_pool = mempool_create_kmalloc_pool(600,
+                       sizeof(struct object_alias_item));
+       if (!super->s_alias_pool)
+               return -ENOMEM;
+
+       super->s_journal_area = alloc_area(sb);
+       if (!super->s_journal_area)
+               goto err;
+
+       for_each_area(i) {
+               super->s_area[i] = alloc_area(sb);
+               if (!super->s_area[i])
+                       goto err;
+               super->s_area[i]->a_level = GC_LEVEL(i);
+               super->s_area[i]->a_ops = &ostore_area_ops;
+       }
+       btree_init_mempool128(&super->s_object_alias_tree,
+                       super->s_btree_pool);
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               free_area(super->s_area[i]);
+       free_area(super->s_journal_area);
+       mempool_destroy(super->s_alias_pool);
+       return -ENOMEM;
+}
+
+void logfs_cleanup_areas(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int i;
+
+       btree_grim_visitor128(&super->s_object_alias_tree, 0, kill_alias);
+       for_each_area(i)
+               free_area(super->s_area[i]);
+       free_area(super->s_journal_area);
+       destroy_meta_inode(super->s_mapping_inode);
+}
diff --git a/fs/logfs/super.c b/fs/logfs/super.c
new file mode 100644 (file)
index 0000000..c66beab
--- /dev/null
@@ -0,0 +1,650 @@
+/*
+ * fs/logfs/super.c
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2005-2008 Joern Engel <joern@logfs.org>
+ *
+ * Generally contains mount/umount code and also serves as a dump area for
+ * any functions that don't fit elsewhere and neither justify a file of their
+ * own.
+ */
+#include "logfs.h"
+#include <linux/bio.h>
+#include <linux/mtd/mtd.h>
+#include <linux/statfs.h>
+#include <linux/buffer_head.h>
+
+static DEFINE_MUTEX(emergency_mutex);
+static struct page *emergency_page;
+
+struct page *emergency_read_begin(struct address_space *mapping, pgoff_t index)
+{
+       filler_t *filler = (filler_t *)mapping->a_ops->readpage;
+       struct page *page;
+       int err;
+
+       page = read_cache_page(mapping, index, filler, NULL);
+       if (page)
+               return page;
+
+       /* No more pages available, switch to emergency page */
+       printk(KERN_INFO"Logfs: Using emergency page\n");
+       mutex_lock(&emergency_mutex);
+       err = filler(NULL, emergency_page);
+       if (err) {
+               mutex_unlock(&emergency_mutex);
+               printk(KERN_EMERG"Logfs: Error reading emergency page\n");
+               return ERR_PTR(err);
+       }
+       return emergency_page;
+}
+
+void emergency_read_end(struct page *page)
+{
+       if (page == emergency_page)
+               mutex_unlock(&emergency_mutex);
+       else
+               page_cache_release(page);
+}
+
+static void dump_segfile(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_segment_entry se;
+       u32 segno;
+
+       for (segno = 0; segno < super->s_no_segs; segno++) {
+               logfs_get_segment_entry(sb, segno, &se);
+               printk("%3x: %6x %8x", segno, be32_to_cpu(se.ec_level),
+                               be32_to_cpu(se.valid));
+               if (++segno < super->s_no_segs) {
+                       logfs_get_segment_entry(sb, segno, &se);
+                       printk(" %6x %8x", be32_to_cpu(se.ec_level),
+                                       be32_to_cpu(se.valid));
+               }
+               if (++segno < super->s_no_segs) {
+                       logfs_get_segment_entry(sb, segno, &se);
+                       printk(" %6x %8x", be32_to_cpu(se.ec_level),
+                                       be32_to_cpu(se.valid));
+               }
+               if (++segno < super->s_no_segs) {
+                       logfs_get_segment_entry(sb, segno, &se);
+                       printk(" %6x %8x", be32_to_cpu(se.ec_level),
+                                       be32_to_cpu(se.valid));
+               }
+               printk("\n");
+       }
+}
+
+/*
+ * logfs_crash_dump - dump debug information to device
+ *
+ * The LogFS superblock only occupies part of a segment.  This function will
+ * write as much debug information as it can gather into the spare space.
+ */
+void logfs_crash_dump(struct super_block *sb)
+{
+       dump_segfile(sb);
+}
+
+/*
+ * TODO: move to lib/string.c
+ */
+/**
+ * memchr_inv - Find a character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the first character other than @c, or %NULL
+ * if the whole buffer contains just @c.
+ */
+void *memchr_inv(const void *s, int c, size_t n)
+{
+       const unsigned char *p = s;
+       while (n-- != 0)
+               if ((unsigned char)c != *p++)
+                       return (void *)(p - 1);
+
+       return NULL;
+}
+
+/*
+ * FIXME: There should be a reserve for root, similar to ext2.
+ */
+int logfs_statfs(struct dentry *dentry, struct kstatfs *stats)
+{
+       struct super_block *sb = dentry->d_sb;
+       struct logfs_super *super = logfs_super(sb);
+
+       stats->f_type           = LOGFS_MAGIC_U32;
+       stats->f_bsize          = sb->s_blocksize;
+       stats->f_blocks         = super->s_size >> LOGFS_BLOCK_BITS >> 3;
+       stats->f_bfree          = super->s_free_bytes >> sb->s_blocksize_bits;
+       stats->f_bavail         = super->s_free_bytes >> sb->s_blocksize_bits;
+       stats->f_files          = 0;
+       stats->f_ffree          = 0;
+       stats->f_namelen        = LOGFS_MAX_NAMELEN;
+       return 0;
+}
+
+static int logfs_sb_set(struct super_block *sb, void *_super)
+{
+       struct logfs_super *super = _super;
+
+       sb->s_fs_info = super;
+       sb->s_mtd = super->s_mtd;
+       sb->s_bdev = super->s_bdev;
+       return 0;
+}
+
+static int logfs_sb_test(struct super_block *sb, void *_super)
+{
+       struct logfs_super *super = _super;
+       struct mtd_info *mtd = super->s_mtd;
+
+       if (mtd && sb->s_mtd == mtd)
+               return 1;
+       if (super->s_bdev && sb->s_bdev == super->s_bdev)
+               return 1;
+       return 0;
+}
+
+static void set_segment_header(struct logfs_segment_header *sh, u8 type,
+               u8 level, u32 segno, u32 ec)
+{
+       sh->pad = 0;
+       sh->type = type;
+       sh->level = level;
+       sh->segno = cpu_to_be32(segno);
+       sh->ec = cpu_to_be32(ec);
+       sh->gec = cpu_to_be64(segno);
+       sh->crc = logfs_crc32(sh, LOGFS_SEGMENT_HEADERSIZE, 4);
+}
+
+static void logfs_write_ds(struct super_block *sb, struct logfs_disk_super *ds,
+               u32 segno, u32 ec)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_segment_header *sh = &ds->ds_sh;
+       int i;
+
+       memset(ds, 0, sizeof(*ds));
+       set_segment_header(sh, SEG_SUPER, 0, segno, ec);
+
+       ds->ds_ifile_levels     = super->s_ifile_levels;
+       ds->ds_iblock_levels    = super->s_iblock_levels;
+       ds->ds_data_levels      = super->s_data_levels; /* XXX: Remove */
+       ds->ds_segment_shift    = super->s_segshift;
+       ds->ds_block_shift      = sb->s_blocksize_bits;
+       ds->ds_write_shift      = super->s_writeshift;
+       ds->ds_filesystem_size  = cpu_to_be64(super->s_size);
+       ds->ds_segment_size     = cpu_to_be32(super->s_segsize);
+       ds->ds_bad_seg_reserve  = cpu_to_be32(super->s_bad_seg_reserve);
+       ds->ds_feature_incompat = cpu_to_be64(super->s_feature_incompat);
+       ds->ds_feature_ro_compat= cpu_to_be64(super->s_feature_ro_compat);
+       ds->ds_feature_compat   = cpu_to_be64(super->s_feature_compat);
+       ds->ds_feature_flags    = cpu_to_be64(super->s_feature_flags);
+       ds->ds_root_reserve     = cpu_to_be64(super->s_root_reserve);
+       ds->ds_speed_reserve    = cpu_to_be64(super->s_speed_reserve);
+       journal_for_each(i)
+               ds->ds_journal_seg[i] = cpu_to_be32(super->s_journal_seg[i]);
+       ds->ds_magic            = cpu_to_be64(LOGFS_MAGIC);
+       ds->ds_crc = logfs_crc32(ds, sizeof(*ds),
+                       LOGFS_SEGMENT_HEADERSIZE + 12);
+}
+
+static int write_one_sb(struct super_block *sb,
+               struct page *(*find_sb)(struct super_block *sb, u64 *ofs))
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_disk_super *ds;
+       struct logfs_segment_entry se;
+       struct page *page;
+       u64 ofs;
+       u32 ec, segno;
+       int err;
+
+       page = find_sb(sb, &ofs);
+       if (!page)
+               return -EIO;
+       ds = page_address(page);
+       segno = seg_no(sb, ofs);
+       logfs_get_segment_entry(sb, segno, &se);
+       ec = be32_to_cpu(se.ec_level) >> 4;
+       ec++;
+       logfs_set_segment_erased(sb, segno, ec, 0);
+       logfs_write_ds(sb, ds, segno, ec);
+       err = super->s_devops->write_sb(sb, page);
+       page_cache_release(page);
+       return err;
+}
+
+int logfs_write_sb(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int err;
+
+       /* First superblock */
+       err = write_one_sb(sb, super->s_devops->find_first_sb);
+       if (err)
+               return err;
+
+       /* Last superblock */
+       err = write_one_sb(sb, super->s_devops->find_last_sb);
+       if (err)
+               return err;
+       return 0;
+}
+
+static int ds_cmp(const void *ds0, const void *ds1)
+{
+       size_t len = sizeof(struct logfs_disk_super);
+
+       /* We know the segment headers differ, so ignore them */
+       len -= LOGFS_SEGMENT_HEADERSIZE;
+       ds0 += LOGFS_SEGMENT_HEADERSIZE;
+       ds1 += LOGFS_SEGMENT_HEADERSIZE;
+       return memcmp(ds0, ds1, len);
+}
+
+static int logfs_recover_sb(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct logfs_disk_super _ds0, *ds0 = &_ds0;
+       struct logfs_disk_super _ds1, *ds1 = &_ds1;
+       int err, valid0, valid1;
+
+       /* read first superblock */
+       err = wbuf_read(sb, super->s_sb_ofs[0], sizeof(*ds0), ds0);
+       if (err)
+               return err;
+       /* read last superblock */
+       err = wbuf_read(sb, super->s_sb_ofs[1], sizeof(*ds1), ds1);
+       if (err)
+               return err;
+       valid0 = logfs_check_ds(ds0) == 0;
+       valid1 = logfs_check_ds(ds1) == 0;
+
+       if (!valid0 && valid1) {
+               printk(KERN_INFO"First superblock is invalid - fixing.\n");
+               return write_one_sb(sb, super->s_devops->find_first_sb);
+       }
+       if (valid0 && !valid1) {
+               printk(KERN_INFO"Last superblock is invalid - fixing.\n");
+               return write_one_sb(sb, super->s_devops->find_last_sb);
+       }
+       if (valid0 && valid1 && ds_cmp(ds0, ds1)) {
+               printk(KERN_INFO"Superblocks don't match - fixing.\n");
+               return write_one_sb(sb, super->s_devops->find_last_sb);
+       }
+       /* If neither is valid now, something's wrong.  Didn't we properly
+        * check them before?!? */
+       BUG_ON(!valid0 && !valid1);
+       return 0;
+}
+
+static int logfs_make_writeable(struct super_block *sb)
+{
+       int err;
+
+       /* Repair any broken superblock copies */
+       err = logfs_recover_sb(sb);
+       if (err)
+               return err;
+
+       /* Check areas for trailing unaccounted data */
+       err = logfs_check_areas(sb);
+       if (err)
+               return err;
+
+       err = logfs_open_segfile(sb);
+       if (err)
+               return err;
+
+       /* Do one GC pass before any data gets dirtied */
+       logfs_gc_pass(sb);
+
+       /* after all initializations are done, replay the journal
+        * for rw-mounts, if necessary */
+       err = logfs_replay_journal(sb);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct inode *rootdir;
+       int err;
+
+       /* root dir */
+       rootdir = logfs_iget(sb, LOGFS_INO_ROOT);
+       if (IS_ERR(rootdir))
+               goto fail;
+
+       sb->s_root = d_alloc_root(rootdir);
+       if (!sb->s_root)
+               goto fail;
+
+       super->s_erase_page = alloc_pages(GFP_KERNEL, 0);
+       if (!super->s_erase_page)
+               goto fail2;
+       memset(page_address(super->s_erase_page), 0xFF, PAGE_SIZE);
+
+       /* FIXME: check for read-only mounts */
+       err = logfs_make_writeable(sb);
+       if (err)
+               goto fail3;
+
+       log_super("LogFS: Finished mounting\n");
+       simple_set_mnt(mnt, sb);
+       return 0;
+
+fail3:
+       __free_page(super->s_erase_page);
+fail2:
+       iput(rootdir);
+fail:
+       iput(logfs_super(sb)->s_master_inode);
+       return -EIO;
+}
+
+int logfs_check_ds(struct logfs_disk_super *ds)
+{
+       struct logfs_segment_header *sh = &ds->ds_sh;
+
+       if (ds->ds_magic != cpu_to_be64(LOGFS_MAGIC))
+               return -EINVAL;
+       if (sh->crc != logfs_crc32(sh, LOGFS_SEGMENT_HEADERSIZE, 4))
+               return -EINVAL;
+       if (ds->ds_crc != logfs_crc32(ds, sizeof(*ds),
+                               LOGFS_SEGMENT_HEADERSIZE + 12))
+               return -EINVAL;
+       return 0;
+}
+
+static struct page *find_super_block(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct page *first, *last;
+
+       first = super->s_devops->find_first_sb(sb, &super->s_sb_ofs[0]);
+       if (!first || IS_ERR(first))
+               return NULL;
+       last = super->s_devops->find_last_sb(sb, &super->s_sb_ofs[1]);
+       if (!last || IS_ERR(first)) {
+               page_cache_release(first);
+               return NULL;
+       }
+
+       if (!logfs_check_ds(page_address(first))) {
+               page_cache_release(last);
+               return first;
+       }
+
+       /* First one didn't work, try the second superblock */
+       if (!logfs_check_ds(page_address(last))) {
+               page_cache_release(first);
+               return last;
+       }
+
+       /* Neither worked, sorry folks */
+       page_cache_release(first);
+       page_cache_release(last);
+       return NULL;
+}
+
+static int __logfs_read_sb(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+       struct page *page;
+       struct logfs_disk_super *ds;
+       int i;
+
+       page = find_super_block(sb);
+       if (!page)
+               return -EIO;
+
+       ds = page_address(page);
+       super->s_size = be64_to_cpu(ds->ds_filesystem_size);
+       super->s_root_reserve = be64_to_cpu(ds->ds_root_reserve);
+       super->s_speed_reserve = be64_to_cpu(ds->ds_speed_reserve);
+       super->s_bad_seg_reserve = be32_to_cpu(ds->ds_bad_seg_reserve);
+       super->s_segsize = 1 << ds->ds_segment_shift;
+       super->s_segmask = (1 << ds->ds_segment_shift) - 1;
+       super->s_segshift = ds->ds_segment_shift;
+       sb->s_blocksize = 1 << ds->ds_block_shift;
+       sb->s_blocksize_bits = ds->ds_block_shift;
+       super->s_writesize = 1 << ds->ds_write_shift;
+       super->s_writeshift = ds->ds_write_shift;
+       super->s_no_segs = super->s_size >> super->s_segshift;
+       super->s_no_blocks = super->s_segsize >> sb->s_blocksize_bits;
+       super->s_feature_incompat = be64_to_cpu(ds->ds_feature_incompat);
+       super->s_feature_ro_compat = be64_to_cpu(ds->ds_feature_ro_compat);
+       super->s_feature_compat = be64_to_cpu(ds->ds_feature_compat);
+       super->s_feature_flags = be64_to_cpu(ds->ds_feature_flags);
+
+       journal_for_each(i)
+               super->s_journal_seg[i] = be32_to_cpu(ds->ds_journal_seg[i]);
+
+       super->s_ifile_levels = ds->ds_ifile_levels;
+       super->s_iblock_levels = ds->ds_iblock_levels;
+       super->s_data_levels = ds->ds_data_levels;
+       super->s_total_levels = super->s_ifile_levels + super->s_iblock_levels
+               + super->s_data_levels;
+       page_cache_release(page);
+       return 0;
+}
+
+static int logfs_read_sb(struct super_block *sb, int read_only)
+{
+       struct logfs_super *super = logfs_super(sb);
+       int ret;
+
+       super->s_btree_pool = mempool_create(32, btree_alloc, btree_free, NULL);
+       if (!super->s_btree_pool)
+               return -ENOMEM;
+
+       btree_init_mempool64(&super->s_shadow_tree.new, super->s_btree_pool);
+       btree_init_mempool64(&super->s_shadow_tree.old, super->s_btree_pool);
+
+       ret = logfs_init_mapping(sb);
+       if (ret)
+               return ret;
+
+       ret = __logfs_read_sb(sb);
+       if (ret)
+               return ret;
+
+       if (super->s_feature_incompat & ~LOGFS_FEATURES_INCOMPAT)
+               return -EIO;
+       if ((super->s_feature_ro_compat & ~LOGFS_FEATURES_RO_COMPAT) &&
+                       !read_only)
+               return -EIO;
+
+       mutex_init(&super->s_dirop_mutex);
+       mutex_init(&super->s_object_alias_mutex);
+       INIT_LIST_HEAD(&super->s_freeing_list);
+
+       ret = logfs_init_rw(sb);
+       if (ret)
+               return ret;
+
+       ret = logfs_init_areas(sb);
+       if (ret)
+               return ret;
+
+       ret = logfs_init_gc(sb);
+       if (ret)
+               return ret;
+
+       ret = logfs_init_journal(sb);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void logfs_kill_sb(struct super_block *sb)
+{
+       struct logfs_super *super = logfs_super(sb);
+
+       log_super("LogFS: Start unmounting\n");
+       /* Alias entries slow down mount, so evict as many as possible */
+       sync_filesystem(sb);
+       logfs_write_anchor(sb);
+
+       /*
+        * From this point on alias entries are simply dropped - and any
+        * writes to the object store are considered bugs.
+        */
+       super->s_flags |= LOGFS_SB_FLAG_SHUTDOWN;
+       log_super("LogFS: Now in shutdown\n");
+       generic_shutdown_super(sb);
+
+       BUG_ON(super->s_dirty_used_bytes || super->s_dirty_free_bytes);
+
+       logfs_cleanup_gc(sb);
+       logfs_cleanup_journal(sb);
+       logfs_cleanup_areas(sb);
+       logfs_cleanup_rw(sb);
+       if (super->s_erase_page)
+               __free_page(super->s_erase_page);
+       super->s_devops->put_device(sb);
+       mempool_destroy(super->s_btree_pool);
+       mempool_destroy(super->s_alias_pool);
+       kfree(super);
+       log_super("LogFS: Finished unmounting\n");
+}
+
+int logfs_get_sb_device(struct file_system_type *type, int flags,
+               struct mtd_info *mtd, struct block_device *bdev,
+               const struct logfs_device_ops *devops, struct vfsmount *mnt)
+{
+       struct logfs_super *super;
+       struct super_block *sb;
+       int err = -ENOMEM;
+       static int mount_count;
+
+       log_super("LogFS: Start mount %x\n", mount_count++);
+       super = kzalloc(sizeof(*super), GFP_KERNEL);
+       if (!super)
+               goto err0;
+
+       super->s_mtd    = mtd;
+       super->s_bdev   = bdev;
+       err = -EINVAL;
+       sb = sget(type, logfs_sb_test, logfs_sb_set, super);
+       if (IS_ERR(sb))
+               goto err0;
+
+       if (sb->s_root) {
+               /* Device is already in use */
+               err = 0;
+               simple_set_mnt(mnt, sb);
+               goto err0;
+       }
+
+       super->s_devops = devops;
+
+       /*
+        * sb->s_maxbytes is limited to 8TB.  On 32bit systems, the page cache
+        * only covers 16TB and the upper 8TB are used for indirect blocks.
+        * On 64bit system we could bump up the limit, but that would make
+        * the filesystem incompatible with 32bit systems.
+        */
+       sb->s_maxbytes  = (1ull << 43) - 1;
+       sb->s_op        = &logfs_super_operations;
+       sb->s_flags     = flags | MS_NOATIME;
+
+       err = logfs_read_sb(sb, sb->s_flags & MS_RDONLY);
+       if (err)
+               goto err1;
+
+       sb->s_flags |= MS_ACTIVE;
+       err = logfs_get_sb_final(sb, mnt);
+       if (err)
+               goto err1;
+       return 0;
+
+err1:
+       up_write(&sb->s_umount);
+       deactivate_super(sb);
+       return err;
+err0:
+       kfree(super);
+       //devops->put_device(sb);
+       return err;
+}
+
+static int logfs_get_sb(struct file_system_type *type, int flags,
+               const char *devname, void *data, struct vfsmount *mnt)
+{
+       ulong mtdnr;
+
+       if (!devname)
+               return logfs_get_sb_bdev(type, flags, devname, mnt);
+       if (strncmp(devname, "mtd", 3))
+               return logfs_get_sb_bdev(type, flags, devname, mnt);
+
+       {
+               char *garbage;
+               mtdnr = simple_strtoul(devname+3, &garbage, 0);
+               if (*garbage)
+                       return -EINVAL;
+       }
+
+       return logfs_get_sb_mtd(type, flags, mtdnr, mnt);
+}
+
+static struct file_system_type logfs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "logfs",
+       .get_sb         = logfs_get_sb,
+       .kill_sb        = logfs_kill_sb,
+       .fs_flags       = FS_REQUIRES_DEV,
+
+};
+
+static int __init logfs_init(void)
+{
+       int ret;
+
+       emergency_page = alloc_pages(GFP_KERNEL, 0);
+       if (!emergency_page)
+               return -ENOMEM;
+
+       ret = logfs_compr_init();
+       if (ret)
+               goto out1;
+
+       ret = logfs_init_inode_cache();
+       if (ret)
+               goto out2;
+
+       return register_filesystem(&logfs_fs_type);
+out2:
+       logfs_compr_exit();
+out1:
+       __free_pages(emergency_page, 0);
+       return ret;
+}
+
+static void __exit logfs_exit(void)
+{
+       unregister_filesystem(&logfs_fs_type);
+       logfs_destroy_inode_cache();
+       logfs_compr_exit();
+       __free_pages(emergency_page, 0);
+}
+
+module_init(logfs_init);
+module_exit(logfs_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Joern Engel <joern@logfs.org>");
+MODULE_DESCRIPTION("scalable flash filesystem");
index 74ea82d72164a09e3d1b9ec0a0c9be84a0d0ac5f..756f8c93780ca5232cbfba4b85128d30cd79b3ba 100644 (file)
 #include <linux/init.h>
 #include <linux/highuid.h>
 #include <linux/vfs.h>
+#include <linux/writeback.h>
 
-static int minix_write_inode(struct inode * inode, int wait);
+static int minix_write_inode(struct inode *inode,
+               struct writeback_control *wbc);
 static int minix_statfs(struct dentry *dentry, struct kstatfs *buf);
 static int minix_remount (struct super_block * sb, int * flags, char * data);
 
@@ -552,7 +554,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
        return bh;
 }
 
-static int minix_write_inode(struct inode *inode, int wait)
+static int minix_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        int err = 0;
        struct buffer_head *bh;
@@ -563,7 +565,7 @@ static int minix_write_inode(struct inode *inode, int wait)
                bh = V2_minix_update_inode(inode);
        if (!bh)
                return -EIO;
-       if (wait && buffer_dirty(bh)) {
+       if (wbc->sync_mode == WB_SYNC_ALL && buffer_dirty(bh)) {
                sync_dirty_buffer(bh);
                if (buffer_req(bh) && !buffer_uptodate(bh)) {
                        printk("IO error syncing minix inode [%s:%08lx]\n",
index a4855af776a8b6670b753aa3af71203519de94a7..48e60a187325e00abb01b2c1278d0cb5441bc6d5 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
-#include <linux/quotaops.h>
 #include <linux/pagemap.h>
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
@@ -498,8 +497,6 @@ static int link_path_walk(const char *, struct nameidata *);
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
-       int res = 0;
-       char *name;
        if (IS_ERR(link))
                goto fail;
 
@@ -510,22 +507,7 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                path_get(&nd->root);
        }
 
-       res = link_path_walk(link, nd);
-       if (nd->depth || res || nd->last_type!=LAST_NORM)
-               return res;
-       /*
-        * If it is an iterative symlinks resolution in open_namei() we
-        * have to copy the last component. And all that crap because of
-        * bloody create() on broken symlinks. Furrfu...
-        */
-       name = __getname();
-       if (unlikely(!name)) {
-               path_put(&nd->path);
-               return -ENOMEM;
-       }
-       strcpy(name, nd->last.name);
-       nd->last.name = name;
-       return 0;
+       return link_path_walk(link, nd);
 fail:
        path_put(&nd->path);
        return PTR_ERR(link);
@@ -547,10 +529,10 @@ static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
        nd->path.dentry = path->dentry;
 }
 
-static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
+static __always_inline int
+__do_follow_link(struct path *path, struct nameidata *nd, void **p)
 {
        int error;
-       void *cookie;
        struct dentry *dentry = path->dentry;
 
        touch_atime(path->mnt, dentry);
@@ -562,9 +544,9 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
        }
        mntget(path->mnt);
        nd->last_type = LAST_BIND;
-       cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
-       error = PTR_ERR(cookie);
-       if (!IS_ERR(cookie)) {
+       *p = dentry->d_inode->i_op->follow_link(dentry, nd);
+       error = PTR_ERR(*p);
+       if (!IS_ERR(*p)) {
                char *s = nd_get_link(nd);
                error = 0;
                if (s)
@@ -574,8 +556,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
                        if (error)
                                path_put(&nd->path);
                }
-               if (dentry->d_inode->i_op->put_link)
-                       dentry->d_inode->i_op->put_link(dentry, nd, cookie);
        }
        return error;
 }
@@ -589,6 +569,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
  */
 static inline int do_follow_link(struct path *path, struct nameidata *nd)
 {
+       void *cookie;
        int err = -ELOOP;
        if (current->link_count >= MAX_NESTED_LINKS)
                goto loop;
@@ -602,7 +583,9 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd)
        current->link_count++;
        current->total_link_count++;
        nd->depth++;
-       err = __do_follow_link(path, nd);
+       err = __do_follow_link(path, nd, &cookie);
+       if (!IS_ERR(cookie) && path->dentry->d_inode->i_op->put_link)
+               path->dentry->d_inode->i_op->put_link(path->dentry, nd, cookie);
        path_put(path);
        current->link_count--;
        nd->depth--;
@@ -689,33 +672,20 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
        set_root(nd);
 
        while(1) {
-               struct vfsmount *parent;
                struct dentry *old = nd->path.dentry;
 
                if (nd->path.dentry == nd->root.dentry &&
                    nd->path.mnt == nd->root.mnt) {
                        break;
                }
-               spin_lock(&dcache_lock);
                if (nd->path.dentry != nd->path.mnt->mnt_root) {
-                       nd->path.dentry = dget(nd->path.dentry->d_parent);
-                       spin_unlock(&dcache_lock);
+                       /* rare case of legitimate dget_parent()... */
+                       nd->path.dentry = dget_parent(nd->path.dentry);
                        dput(old);
                        break;
                }
-               spin_unlock(&dcache_lock);
-               spin_lock(&vfsmount_lock);
-               parent = nd->path.mnt->mnt_parent;
-               if (parent == nd->path.mnt) {
-                       spin_unlock(&vfsmount_lock);
+               if (!follow_up(&nd->path))
                        break;
-               }
-               mntget(parent);
-               nd->path.dentry = dget(nd->path.mnt->mnt_mountpoint);
-               spin_unlock(&vfsmount_lock);
-               dput(old);
-               mntput(nd->path.mnt);
-               nd->path.mnt = parent;
        }
        follow_mount(&nd->path);
 }
@@ -1347,7 +1317,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
                return -ENOENT;
 
        BUG_ON(victim->d_parent->d_inode != dir);
-       audit_inode_child(victim->d_name.name, victim, dir);
+       audit_inode_child(victim, dir);
 
        error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
        if (error)
@@ -1388,22 +1358,6 @@ static inline int may_create(struct inode *dir, struct dentry *child)
        return inode_permission(dir, MAY_WRITE | MAY_EXEC);
 }
 
-/* 
- * O_DIRECTORY translates into forcing a directory lookup.
- */
-static inline int lookup_flags(unsigned int f)
-{
-       unsigned long retval = LOOKUP_FOLLOW;
-
-       if (f & O_NOFOLLOW)
-               retval &= ~LOOKUP_FOLLOW;
-       
-       if (f & O_DIRECTORY)
-               retval |= LOOKUP_DIRECTORY;
-
-       return retval;
-}
-
 /*
  * p1 and p2 should be directories on the same fs.
  */
@@ -1461,7 +1415,6 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
        error = security_inode_create(dir, dentry, mode);
        if (error)
                return error;
-       vfs_dq_init(dir);
        error = dir->i_op->create(dir, dentry, mode, nd);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -1503,7 +1456,7 @@ int may_open(struct path *path, int acc_mode, int flag)
         * An append-only file must be opened in append mode for writing.
         */
        if (IS_APPEND(inode)) {
-               if  ((flag & FMODE_WRITE) && !(flag & O_APPEND))
+               if  ((flag & O_ACCMODE) != O_RDONLY && !(flag & O_APPEND))
                        return -EPERM;
                if (flag & O_TRUNC)
                        return -EPERM;
@@ -1547,7 +1500,7 @@ static int handle_truncate(struct path *path)
  * what get passed to sys_open().
  */
 static int __open_namei_create(struct nameidata *nd, struct path *path,
-                               int flag, int mode)
+                               int open_flag, int mode)
 {
        int error;
        struct dentry *dir = nd->path.dentry;
@@ -1565,7 +1518,7 @@ out_unlock:
        if (error)
                return error;
        /* Don't check for write permission, don't truncate */
-       return may_open(&nd->path, 0, flag & ~O_TRUNC);
+       return may_open(&nd->path, 0, open_flag & ~O_TRUNC);
 }
 
 /*
@@ -1603,129 +1556,132 @@ static int open_will_truncate(int flag, struct inode *inode)
        return (flag & O_TRUNC);
 }
 
-/*
- * Note that the low bits of the passed in "open_flag"
- * are not the same as in the local variable "flag". See
- * open_to_namei_flags() for more details.
- */
-struct file *do_filp_open(int dfd, const char *pathname,
-               int open_flag, int mode, int acc_mode)
+static struct file *finish_open(struct nameidata *nd,
+                               int open_flag, int acc_mode)
 {
        struct file *filp;
-       struct nameidata nd;
-       int error;
-       struct path path;
-       struct dentry *dir;
-       int count = 0;
        int will_truncate;
-       int flag = open_to_namei_flags(open_flag);
-       int force_reval = 0;
+       int error;
 
+       will_truncate = open_will_truncate(open_flag, nd->path.dentry->d_inode);
+       if (will_truncate) {
+               error = mnt_want_write(nd->path.mnt);
+               if (error)
+                       goto exit;
+       }
+       error = may_open(&nd->path, acc_mode, open_flag);
+       if (error) {
+               if (will_truncate)
+                       mnt_drop_write(nd->path.mnt);
+               goto exit;
+       }
+       filp = nameidata_to_filp(nd);
+       if (!IS_ERR(filp)) {
+               error = ima_file_check(filp, acc_mode);
+               if (error) {
+                       fput(filp);
+                       filp = ERR_PTR(error);
+               }
+       }
+       if (!IS_ERR(filp)) {
+               if (will_truncate) {
+                       error = handle_truncate(&nd->path);
+                       if (error) {
+                               fput(filp);
+                               filp = ERR_PTR(error);
+                       }
+               }
+       }
        /*
-        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
-        * check for O_DSYNC if the need any syncing at all we enforce it's
-        * always set instead of having to deal with possibly weird behaviour
-        * for malicious applications setting only __O_SYNC.
+        * It is now safe to drop the mnt write
+        * because the filp has had a write taken
+        * on its behalf.
         */
-       if (open_flag & __O_SYNC)
-               open_flag |= O_DSYNC;
-
-       if (!acc_mode)
-               acc_mode = MAY_OPEN | ACC_MODE(open_flag);
+       if (will_truncate)
+               mnt_drop_write(nd->path.mnt);
+       return filp;
 
-       /* O_TRUNC implies we need access checks for write permissions */
-       if (flag & O_TRUNC)
-               acc_mode |= MAY_WRITE;
+exit:
+       if (!IS_ERR(nd->intent.open.file))
+               release_open_intent(nd);
+       path_put(&nd->path);
+       return ERR_PTR(error);
+}
 
-       /* Allow the LSM permission hook to distinguish append 
-          access from general write access. */
-       if (flag & O_APPEND)
-               acc_mode |= MAY_APPEND;
+static struct file *do_last(struct nameidata *nd, struct path *path,
+                           int open_flag, int acc_mode,
+                           int mode, const char *pathname,
+                           int *want_dir)
+{
+       struct dentry *dir = nd->path.dentry;
+       struct file *filp;
+       int error = -EISDIR;
 
-       /*
-        * The simplest case - just a plain lookup.
-        */
-       if (!(flag & O_CREAT)) {
-               filp = get_empty_filp();
-
-               if (filp == NULL)
-                       return ERR_PTR(-ENFILE);
-               nd.intent.open.file = filp;
-               filp->f_flags = open_flag;
-               nd.intent.open.flags = flag;
-               nd.intent.open.create_mode = 0;
-               error = do_path_lookup(dfd, pathname,
-                                       lookup_flags(flag)|LOOKUP_OPEN, &nd);
-               if (IS_ERR(nd.intent.open.file)) {
-                       if (error == 0) {
-                               error = PTR_ERR(nd.intent.open.file);
-                               path_put(&nd.path);
+       switch (nd->last_type) {
+       case LAST_DOTDOT:
+               follow_dotdot(nd);
+               dir = nd->path.dentry;
+               if (nd->path.mnt->mnt_sb->s_type->fs_flags & FS_REVAL_DOT) {
+                       if (!dir->d_op->d_revalidate(dir, nd)) {
+                               error = -ESTALE;
+                               goto exit;
                        }
-               } else if (error)
-                       release_open_intent(&nd);
-               if (error)
-                       return ERR_PTR(error);
+               }
+               /* fallthrough */
+       case LAST_DOT:
+       case LAST_ROOT:
+               if (open_flag & O_CREAT)
+                       goto exit;
+               /* fallthrough */
+       case LAST_BIND:
+               audit_inode(pathname, dir);
                goto ok;
        }
 
-       /*
-        * Create - we need to know the parent.
-        */
-reval:
-       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
-       if (error)
-               return ERR_PTR(error);
-       if (force_reval)
-               nd.flags |= LOOKUP_REVAL;
-       error = path_walk(pathname, &nd);
-       if (error) {
-               if (nd.root.mnt)
-                       path_put(&nd.root);
-               return ERR_PTR(error);
+       /* trailing slashes? */
+       if (nd->last.name[nd->last.len]) {
+               if (open_flag & O_CREAT)
+                       goto exit;
+               *want_dir = 1;
        }
-       if (unlikely(!audit_dummy_context()))
-               audit_inode(pathname, nd.path.dentry);
 
-       /*
-        * We have the parent and last component. First of all, check
-        * that we are not asked to creat(2) an obvious directory - that
-        * will not do.
-        */
-       error = -EISDIR;
-       if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
-               goto exit_parent;
+       /* just plain open? */
+       if (!(open_flag & O_CREAT)) {
+               error = do_lookup(nd, &nd->last, path);
+               if (error)
+                       goto exit;
+               error = -ENOENT;
+               if (!path->dentry->d_inode)
+                       goto exit_dput;
+               if (path->dentry->d_inode->i_op->follow_link)
+                       return NULL;
+               error = -ENOTDIR;
+               if (*want_dir && !path->dentry->d_inode->i_op->lookup)
+                       goto exit_dput;
+               path_to_nameidata(path, nd);
+               audit_inode(pathname, nd->path.dentry);
+               goto ok;
+       }
 
-       error = -ENFILE;
-       filp = get_empty_filp();
-       if (filp == NULL)
-               goto exit_parent;
-       nd.intent.open.file = filp;
-       filp->f_flags = open_flag;
-       nd.intent.open.flags = flag;
-       nd.intent.open.create_mode = mode;
-       dir = nd.path.dentry;
-       nd.flags &= ~LOOKUP_PARENT;
-       nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;
-       if (flag & O_EXCL)
-               nd.flags |= LOOKUP_EXCL;
+       /* OK, it's O_CREAT */
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(&nd);
-       path.mnt = nd.path.mnt;
 
-do_last:
-       error = PTR_ERR(path.dentry);
-       if (IS_ERR(path.dentry)) {
+       path->dentry = lookup_hash(nd);
+       path->mnt = nd->path.mnt;
+
+       error = PTR_ERR(path->dentry);
+       if (IS_ERR(path->dentry)) {
                mutex_unlock(&dir->d_inode->i_mutex);
                goto exit;
        }
 
-       if (IS_ERR(nd.intent.open.file)) {
-               error = PTR_ERR(nd.intent.open.file);
+       if (IS_ERR(nd->intent.open.file)) {
+               error = PTR_ERR(nd->intent.open.file);
                goto exit_mutex_unlock;
        }
 
        /* Negative dentry, just create the file */
-       if (!path.dentry->d_inode) {
+       if (!path->dentry->d_inode) {
                /*
                 * This write is needed to ensure that a
                 * ro->rw transition does not occur between
@@ -1733,18 +1689,16 @@ do_last:
                 * a permanent write count is taken through
                 * the 'struct file' in nameidata_to_filp().
                 */
-               error = mnt_want_write(nd.path.mnt);
+               error = mnt_want_write(nd->path.mnt);
                if (error)
                        goto exit_mutex_unlock;
-               error = __open_namei_create(&nd, &path, flag, mode);
+               error = __open_namei_create(nd, path, open_flag, mode);
                if (error) {
-                       mnt_drop_write(nd.path.mnt);
+                       mnt_drop_write(nd->path.mnt);
                        goto exit;
                }
-               filp = nameidata_to_filp(&nd);
-               mnt_drop_write(nd.path.mnt);
-               if (nd.root.mnt)
-                       path_put(&nd.root);
+               filp = nameidata_to_filp(nd);
+               mnt_drop_write(nd->path.mnt);
                if (!IS_ERR(filp)) {
                        error = ima_file_check(filp, acc_mode);
                        if (error) {
@@ -1759,150 +1713,181 @@ do_last:
         * It already exists.
         */
        mutex_unlock(&dir->d_inode->i_mutex);
-       audit_inode(pathname, path.dentry);
+       audit_inode(pathname, path->dentry);
 
        error = -EEXIST;
-       if (flag & O_EXCL)
+       if (open_flag & O_EXCL)
                goto exit_dput;
 
-       if (__follow_mount(&path)) {
+       if (__follow_mount(path)) {
                error = -ELOOP;
-               if (flag & O_NOFOLLOW)
+               if (open_flag & O_NOFOLLOW)
                        goto exit_dput;
        }
 
        error = -ENOENT;
-       if (!path.dentry->d_inode)
+       if (!path->dentry->d_inode)
                goto exit_dput;
-       if (path.dentry->d_inode->i_op->follow_link)
-               goto do_link;
 
-       path_to_nameidata(&path, &nd);
+       if (path->dentry->d_inode->i_op->follow_link)
+               return NULL;
+
+       path_to_nameidata(path, nd);
        error = -EISDIR;
-       if (S_ISDIR(path.dentry->d_inode->i_mode))
+       if (S_ISDIR(path->dentry->d_inode->i_mode))
                goto exit;
 ok:
+       filp = finish_open(nd, open_flag, acc_mode);
+       return filp;
+
+exit_mutex_unlock:
+       mutex_unlock(&dir->d_inode->i_mutex);
+exit_dput:
+       path_put_conditional(path, nd);
+exit:
+       if (!IS_ERR(nd->intent.open.file))
+               release_open_intent(nd);
+       path_put(&nd->path);
+       return ERR_PTR(error);
+}
+
+/*
+ * Note that the low bits of the passed in "open_flag"
+ * are not the same as in the local variable "flag". See
+ * open_to_namei_flags() for more details.
+ */
+struct file *do_filp_open(int dfd, const char *pathname,
+               int open_flag, int mode, int acc_mode)
+{
+       struct file *filp;
+       struct nameidata nd;
+       int error;
+       struct path path;
+       int count = 0;
+       int flag = open_to_namei_flags(open_flag);
+       int force_reval = 0;
+       int want_dir = open_flag & O_DIRECTORY;
+
+       if (!(open_flag & O_CREAT))
+               mode = 0;
+
        /*
-        * Consider:
-        * 1. may_open() truncates a file
-        * 2. a rw->ro mount transition occurs
-        * 3. nameidata_to_filp() fails due to
-        *    the ro mount.
-        * That would be inconsistent, and should
-        * be avoided. Taking this mnt write here
-        * ensures that (2) can not occur.
+        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
+        * check for O_DSYNC if the need any syncing at all we enforce it's
+        * always set instead of having to deal with possibly weird behaviour
+        * for malicious applications setting only __O_SYNC.
         */
-       will_truncate = open_will_truncate(flag, nd.path.dentry->d_inode);
-       if (will_truncate) {
-               error = mnt_want_write(nd.path.mnt);
-               if (error)
-                       goto exit;
-       }
-       error = may_open(&nd.path, acc_mode, flag);
+       if (open_flag & __O_SYNC)
+               open_flag |= O_DSYNC;
+
+       if (!acc_mode)
+               acc_mode = MAY_OPEN | ACC_MODE(open_flag);
+
+       /* O_TRUNC implies we need access checks for write permissions */
+       if (open_flag & O_TRUNC)
+               acc_mode |= MAY_WRITE;
+
+       /* Allow the LSM permission hook to distinguish append 
+          access from general write access. */
+       if (open_flag & O_APPEND)
+               acc_mode |= MAY_APPEND;
+
+       /* find the parent */
+reval:
+       error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
+       if (error)
+               return ERR_PTR(error);
+       if (force_reval)
+               nd.flags |= LOOKUP_REVAL;
+
+       current->total_link_count = 0;
+       error = link_path_walk(pathname, &nd);
        if (error) {
-               if (will_truncate)
-                       mnt_drop_write(nd.path.mnt);
-               goto exit;
-       }
-       filp = nameidata_to_filp(&nd);
-       if (!IS_ERR(filp)) {
-               error = ima_file_check(filp, acc_mode);
-               if (error) {
-                       fput(filp);
-                       filp = ERR_PTR(error);
-               }
+               filp = ERR_PTR(error);
+               goto out;
        }
-       if (!IS_ERR(filp)) {
-               if (acc_mode & MAY_WRITE)
-                       vfs_dq_init(nd.path.dentry->d_inode);
+       if (unlikely(!audit_dummy_context()) && (open_flag & O_CREAT))
+               audit_inode(pathname, nd.path.dentry);
 
-               if (will_truncate) {
-                       error = handle_truncate(&nd.path);
-                       if (error) {
-                               fput(filp);
-                               filp = ERR_PTR(error);
-                       }
-               }
-       }
        /*
-        * It is now safe to drop the mnt write
-        * because the filp has had a write taken
-        * on its behalf.
+        * We have the parent and last component.
         */
-       if (will_truncate)
-               mnt_drop_write(nd.path.mnt);
+
+       error = -ENFILE;
+       filp = get_empty_filp();
+       if (filp == NULL)
+               goto exit_parent;
+       nd.intent.open.file = filp;
+       filp->f_flags = open_flag;
+       nd.intent.open.flags = flag;
+       nd.intent.open.create_mode = mode;
+       nd.flags &= ~LOOKUP_PARENT;
+       nd.flags |= LOOKUP_OPEN;
+       if (open_flag & O_CREAT) {
+               nd.flags |= LOOKUP_CREATE;
+               if (open_flag & O_EXCL)
+                       nd.flags |= LOOKUP_EXCL;
+       }
+       filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &want_dir);
+       while (unlikely(!filp)) { /* trailing symlink */
+               struct path holder;
+               struct inode *inode = path.dentry->d_inode;
+               void *cookie;
+               error = -ELOOP;
+               /* S_ISDIR part is a temporary automount kludge */
+               if ((open_flag & O_NOFOLLOW) && !S_ISDIR(inode->i_mode))
+                       goto exit_dput;
+               if (count++ == 32)
+                       goto exit_dput;
+               /*
+                * This is subtle. Instead of calling do_follow_link() we do
+                * the thing by hands. The reason is that this way we have zero
+                * link_count and path_walk() (called from ->follow_link)
+                * honoring LOOKUP_PARENT.  After that we have the parent and
+                * last component, i.e. we are in the same situation as after
+                * the first path_walk().  Well, almost - if the last component
+                * is normal we get its copy stored in nd->last.name and we will
+                * have to putname() it when we are done. Procfs-like symlinks
+                * just set LAST_BIND.
+                */
+               nd.flags |= LOOKUP_PARENT;
+               error = security_inode_follow_link(path.dentry, &nd);
+               if (error)
+                       goto exit_dput;
+               error = __do_follow_link(&path, &nd, &cookie);
+               if (unlikely(error)) {
+                       /* nd.path had been dropped */
+                       if (!IS_ERR(cookie) && inode->i_op->put_link)
+                               inode->i_op->put_link(path.dentry, &nd, cookie);
+                       path_put(&path);
+                       release_open_intent(&nd);
+                       filp = ERR_PTR(error);
+                       goto out;
+               }
+               holder = path;
+               nd.flags &= ~LOOKUP_PARENT;
+               filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname, &want_dir);
+               if (inode->i_op->put_link)
+                       inode->i_op->put_link(holder.dentry, &nd, cookie);
+               path_put(&holder);
+       }
+out:
        if (nd.root.mnt)
                path_put(&nd.root);
+       if (filp == ERR_PTR(-ESTALE) && !force_reval) {
+               force_reval = 1;
+               goto reval;
+       }
        return filp;
 
-exit_mutex_unlock:
-       mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
        path_put_conditional(&path, &nd);
-exit:
        if (!IS_ERR(nd.intent.open.file))
                release_open_intent(&nd);
 exit_parent:
-       if (nd.root.mnt)
-               path_put(&nd.root);
        path_put(&nd.path);
-       return ERR_PTR(error);
-
-do_link:
-       error = -ELOOP;
-       if (flag & O_NOFOLLOW)
-               goto exit_dput;
-       /*
-        * This is subtle. Instead of calling do_follow_link() we do the
-        * thing by hands. The reason is that this way we have zero link_count
-        * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
-        * After that we have the parent and last component, i.e.
-        * we are in the same situation as after the first path_walk().
-        * Well, almost - if the last component is normal we get its copy
-        * stored in nd->last.name and we will have to putname() it when we
-        * are done. Procfs-like symlinks just set LAST_BIND.
-        */
-       nd.flags |= LOOKUP_PARENT;
-       error = security_inode_follow_link(path.dentry, &nd);
-       if (error)
-               goto exit_dput;
-       error = __do_follow_link(&path, &nd);
-       path_put(&path);
-       if (error) {
-               /* Does someone understand code flow here? Or it is only
-                * me so stupid? Anathema to whoever designed this non-sense
-                * with "intent.open".
-                */
-               release_open_intent(&nd);
-               if (nd.root.mnt)
-                       path_put(&nd.root);
-               if (error == -ESTALE && !force_reval) {
-                       force_reval = 1;
-                       goto reval;
-               }
-               return ERR_PTR(error);
-       }
-       nd.flags &= ~LOOKUP_PARENT;
-       if (nd.last_type == LAST_BIND)
-               goto ok;
-       error = -EISDIR;
-       if (nd.last_type != LAST_NORM)
-               goto exit;
-       if (nd.last.name[nd.last.len]) {
-               __putname(nd.last.name);
-               goto exit;
-       }
-       error = -ELOOP;
-       if (count++==32) {
-               __putname(nd.last.name);
-               goto exit;
-       }
-       dir = nd.path.dentry;
-       mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(&nd);
-       path.mnt = nd.path.mnt;
-       __putname(nd.last.name);
-       goto do_last;
+       filp = ERR_PTR(error);
+       goto out;
 }
 
 /**
@@ -1996,7 +1981,6 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if (error)
                return error;
 
-       vfs_dq_init(dir);
        error = dir->i_op->mknod(dir, dentry, mode, dev);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -2095,7 +2079,6 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        if (error)
                return error;
 
-       vfs_dq_init(dir);
        error = dir->i_op->mkdir(dir, dentry, mode);
        if (!error)
                fsnotify_mkdir(dir, dentry);
@@ -2181,8 +2164,6 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (!dir->i_op->rmdir)
                return -EPERM;
 
-       vfs_dq_init(dir);
-
        mutex_lock(&dentry->d_inode->i_mutex);
        dentry_unhash(dentry);
        if (d_mountpoint(dentry))
@@ -2268,15 +2249,16 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
        if (!dir->i_op->unlink)
                return -EPERM;
 
-       vfs_dq_init(dir);
-
        mutex_lock(&dentry->d_inode->i_mutex);
        if (d_mountpoint(dentry))
                error = -EBUSY;
        else {
                error = security_inode_unlink(dir, dentry);
-               if (!error)
+               if (!error) {
                        error = dir->i_op->unlink(dir, dentry);
+                       if (!error)
+                               dentry->d_inode->i_flags |= S_DEAD;
+               }
        }
        mutex_unlock(&dentry->d_inode->i_mutex);
 
@@ -2379,7 +2361,6 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
        if (error)
                return error;
 
-       vfs_dq_init(dir);
        error = dir->i_op->symlink(dir, dentry, oldname);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -2463,7 +2444,6 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
                return error;
 
        mutex_lock(&inode->i_mutex);
-       vfs_dq_init(dir);
        error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&inode->i_mutex);
        if (!error)
@@ -2629,6 +2609,8 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
        else
                error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
        if (!error) {
+               if (target)
+                       target->i_flags |= S_DEAD;
                if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
                        d_move(old_dentry, new_dentry);
        }
@@ -2662,20 +2644,15 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (!old_dir->i_op->rename)
                return -EPERM;
 
-       vfs_dq_init(old_dir);
-       vfs_dq_init(new_dir);
-
        old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 
        if (is_dir)
                error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
        else
                error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
-       if (!error) {
-               const char *new_name = old_dentry->d_name.name;
-               fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
+       if (!error)
+               fsnotify_move(old_dir, new_dir, old_name, is_dir,
                              new_dentry->d_inode, old_dentry);
-       }
        fsnotify_oldname_free(old_name);
 
        return error;
index c768f733c8d6585a87551190bdf1b71a14cab9fe..8174c8ab5c70e71316403e2f879a51c77566e422 100644 (file)
@@ -573,7 +573,7 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
                        mnt->mnt_master = old;
                        CLEAR_MNT_SHARED(mnt);
                } else if (!(flag & CL_PRIVATE)) {
-                       if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old))
+                       if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old))
                                list_add(&mnt->mnt_share, &old->mnt_share);
                        if (IS_MNT_SLAVE(old))
                                list_add(&mnt->mnt_slave, &old->mnt_slave);
@@ -737,6 +737,21 @@ static void m_stop(struct seq_file *m, void *v)
        up_read(&namespace_sem);
 }
 
+int mnt_had_events(struct proc_mounts *p)
+{
+       struct mnt_namespace *ns = p->ns;
+       int res = 0;
+
+       spin_lock(&vfsmount_lock);
+       if (p->event != ns->event) {
+               p->event = ns->event;
+               res = 1;
+       }
+       spin_unlock(&vfsmount_lock);
+
+       return res;
+}
+
 struct proc_fs_info {
        int flag;
        const char *str;
@@ -1121,8 +1136,15 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
 {
        struct path path;
        int retval;
+       int lookup_flags = 0;
 
-       retval = user_path(name, &path);
+       if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
+               return -EINVAL;
+
+       if (!(flags & UMOUNT_NOFOLLOW))
+               lookup_flags |= LOOKUP_FOLLOW;
+
+       retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
        if (retval)
                goto out;
        retval = -EINVAL;
@@ -1246,6 +1268,21 @@ void drop_collected_mounts(struct vfsmount *mnt)
        release_mounts(&umount_list);
 }
 
+int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
+                  struct vfsmount *root)
+{
+       struct vfsmount *mnt;
+       int res = f(root, arg);
+       if (res)
+               return res;
+       list_for_each_entry(mnt, &root->mnt_list, mnt_list) {
+               res = f(mnt, arg);
+               if (res)
+                       return res;
+       }
+       return 0;
+}
+
 static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end)
 {
        struct vfsmount *p;
@@ -1538,7 +1575,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
                err = do_remount_sb(sb, flags, data, 0);
        if (!err) {
                spin_lock(&vfsmount_lock);
-               mnt_flags |= path->mnt->mnt_flags & MNT_PNODE_MASK;
+               mnt_flags |= path->mnt->mnt_flags & MNT_PROPAGATION_MASK;
                path->mnt->mnt_flags = mnt_flags;
                spin_unlock(&vfsmount_lock);
        }
@@ -1671,7 +1708,7 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path,
 {
        int err;
 
-       mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD);
+       mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
 
        down_write(&namespace_sem);
        /* Something was mounted here while we slept */
@@ -2314,17 +2351,13 @@ void __init mnt_init(void)
 
 void put_mnt_ns(struct mnt_namespace *ns)
 {
-       struct vfsmount *root;
        LIST_HEAD(umount_list);
 
-       if (!atomic_dec_and_lock(&ns->count, &vfsmount_lock))
+       if (!atomic_dec_and_test(&ns->count))
                return;
-       root = ns->root;
-       ns->root = NULL;
-       spin_unlock(&vfsmount_lock);
        down_write(&namespace_sem);
        spin_lock(&vfsmount_lock);
-       umount_tree(root, 0, &umount_list);
+       umount_tree(ns->root, 0, &umount_list);
        spin_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
        release_mounts(&umount_list);
index 73ab220354dfc6c2a046535ec959707779ed56ed..36dfdae95123308426ccc9db7565a2ceb457b575 100644 (file)
@@ -118,7 +118,6 @@ nfs4_callback_up(struct svc_serv *serv)
        dprintk("NFS: Callback listener port = %u (af %u)\n",
                        nfs_callback_tcpport, PF_INET);
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        ret = svc_create_xprt(serv, "tcp", PF_INET6,
                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
        if (ret > 0) {
@@ -129,7 +128,6 @@ nfs4_callback_up(struct svc_serv *serv)
                ret = 0;
        else
                goto out_err;
-#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
 
        return svc_prepare_thread(serv, &serv->sv_pools[0]);
 
index d4036be0b589ffd24c391b53bded40bc55a6ab5f..85a7cfd1b8dda66bd7a25dcb8e0be91845b191ac 100644 (file)
@@ -119,6 +119,14 @@ struct cb_recallanyargs {
 };
 
 extern unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy);
+
+struct cb_recallslotargs {
+       struct sockaddr *crsa_addr;
+       uint32_t        crsa_target_max_slots;
+};
+extern unsigned nfs4_callback_recallslot(struct cb_recallslotargs *args,
+                                         void *dummy);
+
 #endif /* CONFIG_NFS_V4_1 */
 
 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
index defa9b4c470ebf0515d3b7da58585c0ec551e3db..84761b5bb8e22142fd8eaefa82e48d1af927ee3c 100644 (file)
@@ -143,44 +143,49 @@ int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const n
  * Return success if the sequenceID is one more than what we last saw on
  * this slot, accounting for wraparound.  Increments the slot's sequence.
  *
- * We don't yet implement a duplicate request cache, so at this time
- * we will log replays, and process them as if we had not seen them before,
- * but we don't bump the sequence in the slot.  Not too worried about it,
+ * We don't yet implement a duplicate request cache, instead we set the
+ * back channel ca_maxresponsesize_cached to zero. This is OK for now
  * since we only currently implement idempotent callbacks anyway.
  *
  * We have a single slot backchannel at this time, so we don't bother
  * checking the used_slots bit array on the table.  The lower layer guarantees
  * a single outstanding callback request at a time.
  */
-static int
-validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid)
+static __be32
+validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
 {
        struct nfs4_slot *slot;
 
        dprintk("%s enter. slotid %d seqid %d\n",
-               __func__, slotid, seqid);
+               __func__, args->csa_slotid, args->csa_sequenceid);
 
-       if (slotid > NFS41_BC_MAX_CALLBACKS)
+       if (args->csa_slotid > NFS41_BC_MAX_CALLBACKS)
                return htonl(NFS4ERR_BADSLOT);
 
-       slot = tbl->slots + slotid;
+       slot = tbl->slots + args->csa_slotid;
        dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr);
 
        /* Normal */
-       if (likely(seqid == slot->seq_nr + 1)) {
+       if (likely(args->csa_sequenceid == slot->seq_nr + 1)) {
                slot->seq_nr++;
                return htonl(NFS4_OK);
        }
 
        /* Replay */
-       if (seqid == slot->seq_nr) {
-               dprintk("%s seqid %d is a replay - no DRC available\n",
-                       __func__, seqid);
-               return htonl(NFS4_OK);
+       if (args->csa_sequenceid == slot->seq_nr) {
+               dprintk("%s seqid %d is a replay\n",
+                       __func__, args->csa_sequenceid);
+               /* Signal process_op to set this error on next op */
+               if (args->csa_cachethis == 0)
+                       return htonl(NFS4ERR_RETRY_UNCACHED_REP);
+
+               /* The ca_maxresponsesize_cached is 0 with no DRC */
+               else if (args->csa_cachethis == 1)
+                       return htonl(NFS4ERR_REP_TOO_BIG_TO_CACHE);
        }
 
        /* Wraparound */
-       if (seqid == 1 && (slot->seq_nr + 1) == 0) {
+       if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) {
                slot->seq_nr = 1;
                return htonl(NFS4_OK);
        }
@@ -225,27 +230,87 @@ validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid)
        return NULL;
 }
 
-/* FIXME: referring calls should be processed */
-unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
+/*
+ * For each referring call triple, check the session's slot table for
+ * a match.  If the slot is in use and the sequence numbers match, the
+ * client is still waiting for a response to the original request.
+ */
+static bool referring_call_exists(struct nfs_client *clp,
+                                 uint32_t nrclists,
+                                 struct referring_call_list *rclists)
+{
+       bool status = 0;
+       int i, j;
+       struct nfs4_session *session;
+       struct nfs4_slot_table *tbl;
+       struct referring_call_list *rclist;
+       struct referring_call *ref;
+
+       /*
+        * XXX When client trunking is implemented, this becomes
+        * a session lookup from within the loop
+        */
+       session = clp->cl_session;
+       tbl = &session->fc_slot_table;
+
+       for (i = 0; i < nrclists; i++) {
+               rclist = &rclists[i];
+               if (memcmp(session->sess_id.data,
+                          rclist->rcl_sessionid.data,
+                          NFS4_MAX_SESSIONID_LEN) != 0)
+                       continue;
+
+               for (j = 0; j < rclist->rcl_nrefcalls; j++) {
+                       ref = &rclist->rcl_refcalls[j];
+
+                       dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u "
+                               "slotid %u\n", __func__,
+                               ((u32 *)&rclist->rcl_sessionid.data)[0],
+                               ((u32 *)&rclist->rcl_sessionid.data)[1],
+                               ((u32 *)&rclist->rcl_sessionid.data)[2],
+                               ((u32 *)&rclist->rcl_sessionid.data)[3],
+                               ref->rc_sequenceid, ref->rc_slotid);
+
+                       spin_lock(&tbl->slot_tbl_lock);
+                       status = (test_bit(ref->rc_slotid, tbl->used_slots) &&
+                                 tbl->slots[ref->rc_slotid].seq_nr ==
+                                       ref->rc_sequenceid);
+                       spin_unlock(&tbl->slot_tbl_lock);
+                       if (status)
+                               goto out;
+               }
+       }
+
+out:
+       return status;
+}
+
+__be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
                                struct cb_sequenceres *res)
 {
        struct nfs_client *clp;
-       int i, status;
-
-       for (i = 0; i < args->csa_nrclists; i++)
-               kfree(args->csa_rclists[i].rcl_refcalls);
-       kfree(args->csa_rclists);
+       int i;
+       __be32 status;
 
        status = htonl(NFS4ERR_BADSESSION);
        clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid);
        if (clp == NULL)
                goto out;
 
-       status = validate_seqid(&clp->cl_session->bc_slot_table,
-                               args->csa_slotid, args->csa_sequenceid);
+       status = validate_seqid(&clp->cl_session->bc_slot_table, args);
        if (status)
                goto out_putclient;
 
+       /*
+        * Check for pending referring calls.  If a match is found, a
+        * related callback was received before the response to the original
+        * call.
+        */
+       if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
+               status = htonl(NFS4ERR_DELAY);
+               goto out_putclient;
+       }
+
        memcpy(&res->csr_sessionid, &args->csa_sessionid,
               sizeof(res->csr_sessionid));
        res->csr_sequenceid = args->csa_sequenceid;
@@ -256,15 +321,23 @@ unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
 out_putclient:
        nfs_put_client(clp);
 out:
-       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
-       res->csr_status = status;
-       return res->csr_status;
+       for (i = 0; i < args->csa_nrclists; i++)
+               kfree(args->csa_rclists[i].rcl_refcalls);
+       kfree(args->csa_rclists);
+
+       if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP))
+               res->csr_status = 0;
+       else
+               res->csr_status = status;
+       dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
+               ntohl(status), ntohl(res->csr_status));
+       return status;
 }
 
-unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
+__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
 {
        struct nfs_client *clp;
-       int status;
+       __be32 status;
        fmode_t flags = 0;
 
        status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
@@ -289,4 +362,40 @@ out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
        return status;
 }
+
+/* Reduce the fore channel's max_slots to the target value */
+__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy)
+{
+       struct nfs_client *clp;
+       struct nfs4_slot_table *fc_tbl;
+       __be32 status;
+
+       status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
+       clp = nfs_find_client(args->crsa_addr, 4);
+       if (clp == NULL)
+               goto out;
+
+       dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
+               rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
+               args->crsa_target_max_slots);
+
+       fc_tbl = &clp->cl_session->fc_slot_table;
+
+       status = htonl(NFS4ERR_BAD_HIGH_SLOT);
+       if (args->crsa_target_max_slots > fc_tbl->max_slots ||
+           args->crsa_target_max_slots < 1)
+               goto out_putclient;
+
+       status = htonl(NFS4_OK);
+       if (args->crsa_target_max_slots == fc_tbl->max_slots)
+               goto out_putclient;
+
+       fc_tbl->target_max_slots = args->crsa_target_max_slots;
+       nfs41_handle_recall_slot(clp);
+out_putclient:
+       nfs_put_client(clp);    /* balance nfs_find_client */
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+}
 #endif /* CONFIG_NFS_V4_1 */
index 8e1a2511c8be5b00e35fae972ee62cc921ce2d15..db30c0b398b5809b6bd892f8cc9b8bb987a23603 100644 (file)
 #define CB_OP_SEQUENCE_RES_MAXSZ       (CB_OP_HDR_RES_MAXSZ + \
                                        4 + 1 + 3)
 #define CB_OP_RECALLANY_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
+#define CB_OP_RECALLSLOT_RES_MAXSZ     (CB_OP_HDR_RES_MAXSZ)
 #endif /* CONFIG_NFS_V4_1 */
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 
+/* Internal error code */
+#define NFS4ERR_RESOURCE_HDR   11050
+
 typedef __be32 (*callback_process_op_t)(void *, void *);
 typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
 typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
@@ -173,7 +177,7 @@ static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op)
        __be32 *p;
        p = read_buf(xdr, 4);
        if (unlikely(p == NULL))
-               return htonl(NFS4ERR_RESOURCE);
+               return htonl(NFS4ERR_RESOURCE_HDR);
        *op = ntohl(*p);
        return 0;
 }
@@ -215,10 +219,10 @@ out:
 
 #if defined(CONFIG_NFS_V4_1)
 
-static unsigned decode_sessionid(struct xdr_stream *xdr,
+static __be32 decode_sessionid(struct xdr_stream *xdr,
                                 struct nfs4_sessionid *sid)
 {
-       uint32_t *p;
+       __be32 *p;
        int len = NFS4_MAX_SESSIONID_LEN;
 
        p = read_buf(xdr, len);
@@ -229,12 +233,12 @@ static unsigned decode_sessionid(struct xdr_stream *xdr,
        return 0;
 }
 
-static unsigned decode_rc_list(struct xdr_stream *xdr,
+static __be32 decode_rc_list(struct xdr_stream *xdr,
                               struct referring_call_list *rc_list)
 {
-       uint32_t *p;
+       __be32 *p;
        int i;
-       unsigned status;
+       __be32 status;
 
        status = decode_sessionid(xdr, &rc_list->rcl_sessionid);
        if (status)
@@ -267,13 +271,13 @@ out:
        return status;
 }
 
-static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp,
+static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp,
                                        struct xdr_stream *xdr,
                                        struct cb_sequenceargs *args)
 {
-       uint32_t *p;
+       __be32 *p;
        int i;
-       unsigned status;
+       __be32 status;
 
        status = decode_sessionid(xdr, &args->csa_sessionid);
        if (status)
@@ -327,11 +331,11 @@ out_free:
        goto out;
 }
 
-static unsigned decode_recallany_args(struct svc_rqst *rqstp,
+static __be32 decode_recallany_args(struct svc_rqst *rqstp,
                                      struct xdr_stream *xdr,
                                      struct cb_recallanyargs *args)
 {
-       uint32_t *p;
+       __be32 *p;
 
        args->craa_addr = svc_addr(rqstp);
        p = read_buf(xdr, 4);
@@ -346,6 +350,20 @@ static unsigned decode_recallany_args(struct svc_rqst *rqstp,
        return 0;
 }
 
+static __be32 decode_recallslot_args(struct svc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       struct cb_recallslotargs *args)
+{
+       __be32 *p;
+
+       args->crsa_addr = svc_addr(rqstp);
+       p = read_buf(xdr, 4);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_BADXDR);
+       args->crsa_target_max_slots = ntohl(*p++);
+       return 0;
+}
+
 #endif /* CONFIG_NFS_V4_1 */
 
 static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
@@ -465,7 +483,7 @@ static __be32 encode_op_hdr(struct xdr_stream *xdr, uint32_t op, __be32 res)
        
        p = xdr_reserve_space(xdr, 8);
        if (unlikely(p == NULL))
-               return htonl(NFS4ERR_RESOURCE);
+               return htonl(NFS4ERR_RESOURCE_HDR);
        *p++ = htonl(op);
        *p = res;
        return 0;
@@ -499,10 +517,10 @@ out:
 
 #if defined(CONFIG_NFS_V4_1)
 
-static unsigned encode_sessionid(struct xdr_stream *xdr,
+static __be32 encode_sessionid(struct xdr_stream *xdr,
                                 const struct nfs4_sessionid *sid)
 {
-       uint32_t *p;
+       __be32 *p;
        int len = NFS4_MAX_SESSIONID_LEN;
 
        p = xdr_reserve_space(xdr, len);
@@ -513,11 +531,11 @@ static unsigned encode_sessionid(struct xdr_stream *xdr,
        return 0;
 }
 
-static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp,
+static __be32 encode_cb_sequence_res(struct svc_rqst *rqstp,
                                       struct xdr_stream *xdr,
                                       const struct cb_sequenceres *res)
 {
-       uint32_t *p;
+       __be32 *p;
        unsigned status = res->csr_status;
 
        if (unlikely(status != 0))
@@ -554,6 +572,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_RECALL:
        case OP_CB_SEQUENCE:
        case OP_CB_RECALL_ANY:
+       case OP_CB_RECALL_SLOT:
                *op = &callback_ops[op_nr];
                break;
 
@@ -562,7 +581,6 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_NOTIFY:
        case OP_CB_PUSH_DELEG:
        case OP_CB_RECALLABLE_OBJ_AVAIL:
-       case OP_CB_RECALL_SLOT:
        case OP_CB_WANTS_CANCELLED:
        case OP_CB_NOTIFY_LOCK:
                return htonl(NFS4ERR_NOTSUPP);
@@ -602,20 +620,18 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
 static __be32 process_op(uint32_t minorversion, int nop,
                struct svc_rqst *rqstp,
                struct xdr_stream *xdr_in, void *argp,
-               struct xdr_stream *xdr_out, void *resp)
+               struct xdr_stream *xdr_out, void *resp, int* drc_status)
 {
        struct callback_op *op = &callback_ops[0];
-       unsigned int op_nr = OP_CB_ILLEGAL;
+       unsigned int op_nr;
        __be32 status;
        long maxlen;
        __be32 res;
 
        dprintk("%s: start\n", __func__);
        status = decode_op_hdr(xdr_in, &op_nr);
-       if (unlikely(status)) {
-               status = htonl(NFS4ERR_OP_ILLEGAL);
-               goto out;
-       }
+       if (unlikely(status))
+               return status;
 
        dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
                __func__, minorversion, nop, op_nr);
@@ -624,19 +640,32 @@ static __be32 process_op(uint32_t minorversion, int nop,
                                preprocess_nfs4_op(op_nr, &op);
        if (status == htonl(NFS4ERR_OP_ILLEGAL))
                op_nr = OP_CB_ILLEGAL;
-out:
+       if (status)
+               goto encode_hdr;
+
+       if (*drc_status) {
+               status = *drc_status;
+               goto encode_hdr;
+       }
+
        maxlen = xdr_out->end - xdr_out->p;
        if (maxlen > 0 && maxlen < PAGE_SIZE) {
-               if (likely(status == 0 && op->decode_args != NULL))
-                       status = op->decode_args(rqstp, xdr_in, argp);
-               if (likely(status == 0 && op->process_op != NULL))
+               status = op->decode_args(rqstp, xdr_in, argp);
+               if (likely(status == 0))
                        status = op->process_op(argp, resp);
        } else
                status = htonl(NFS4ERR_RESOURCE);
 
+       /* Only set by OP_CB_SEQUENCE processing */
+       if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
+               *drc_status = status;
+               status = 0;
+       }
+
+encode_hdr:
        res = encode_op_hdr(xdr_out, op_nr, status);
-       if (status == 0)
-               status = res;
+       if (unlikely(res))
+               return res;
        if (op->encode_res != NULL && status == 0)
                status = op->encode_res(rqstp, xdr_out, resp);
        dprintk("%s: done, status = %d\n", __func__, ntohl(status));
@@ -652,7 +681,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        struct cb_compound_hdr_res hdr_res = { NULL };
        struct xdr_stream xdr_in, xdr_out;
        __be32 *p;
-       __be32 status;
+       __be32 status, drc_status = 0;
        unsigned int nops = 0;
 
        dprintk("%s: start\n", __func__);
@@ -672,11 +701,18 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
                return rpc_system_err;
 
        while (status == 0 && nops != hdr_arg.nops) {
-               status = process_op(hdr_arg.minorversion, nops,
-                                   rqstp, &xdr_in, argp, &xdr_out, resp);
+               status = process_op(hdr_arg.minorversion, nops, rqstp,
+                                   &xdr_in, argp, &xdr_out, resp, &drc_status);
                nops++;
        }
 
+       /* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return
+       * resource error in cb_compound status without returning op */
+       if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) {
+               status = htonl(NFS4ERR_RESOURCE);
+               nops--;
+       }
+
        *hdr_res.status = status;
        *hdr_res.nops = htonl(nops);
        dprintk("%s: done, status = %u\n", __func__, ntohl(status));
@@ -713,6 +749,11 @@ static struct callback_op callback_ops[] = {
                .decode_args = (callback_decode_arg_t)decode_recallany_args,
                .res_maxsize = CB_OP_RECALLANY_RES_MAXSZ,
        },
+       [OP_CB_RECALL_SLOT] = {
+               .process_op = (callback_process_op_t)nfs4_callback_recallslot,
+               .decode_args = (callback_decode_arg_t)decode_recallslot_args,
+               .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ,
+       },
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index ee77713ce68b10e020b0589920560f87c6fffe11..2274f1737336485f30fcfd7fdfab841e9bc39600 100644 (file)
@@ -164,30 +164,7 @@ error_0:
        return ERR_PTR(err);
 }
 
-static void nfs4_shutdown_client(struct nfs_client *clp)
-{
-#ifdef CONFIG_NFS_V4
-       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
-               nfs4_kill_renewd(clp);
-       BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners));
-       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
-               nfs_idmap_delete(clp);
-
-       rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
-#endif
-}
-
-/*
- * Destroy the NFS4 callback service
- */
-static void nfs4_destroy_callback(struct nfs_client *clp)
-{
 #ifdef CONFIG_NFS_V4
-       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-               nfs_callback_down(clp->cl_minorversion);
-#endif /* CONFIG_NFS_V4 */
-}
-
 /*
  * Clears/puts all minor version specific parts from an nfs_client struct
  * reverting it to minorversion 0.
@@ -202,9 +179,33 @@ static void nfs4_clear_client_minor_version(struct nfs_client *clp)
 
        clp->cl_call_sync = _nfs4_call_sync;
 #endif /* CONFIG_NFS_V4_1 */
+}
 
+/*
+ * Destroy the NFS4 callback service
+ */
+static void nfs4_destroy_callback(struct nfs_client *clp)
+{
+       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
+               nfs_callback_down(clp->cl_minorversion);
+}
+
+static void nfs4_shutdown_client(struct nfs_client *clp)
+{
+       if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
+               nfs4_kill_renewd(clp);
+       nfs4_clear_client_minor_version(clp);
        nfs4_destroy_callback(clp);
+       if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
+               nfs_idmap_delete(clp);
+
+       rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
 }
+#else
+static void nfs4_shutdown_client(struct nfs_client *clp)
+{
+}
+#endif /* CONFIG_NFS_V4 */
 
 /*
  * Destroy a shared client record
@@ -213,7 +214,6 @@ static void nfs_free_client(struct nfs_client *clp)
 {
        dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
 
-       nfs4_clear_client_minor_version(clp);
        nfs4_shutdown_client(clp);
 
        nfs_fscache_release_client_cookie(clp);
index 3c7f03b669fb62a7a9158b8d699a24249e51c66d..a1f6b4438fb12cbd5b49a648b986f58354079bf0 100644 (file)
@@ -560,7 +560,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        desc->entry = &my_entry;
 
        nfs_block_sillyrename(dentry);
-       res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping);
+       res = nfs_revalidate_mapping(inode, filp->f_mapping);
        if (res < 0)
                goto out;
 
index 95e1ca765d4757085117e8c401fbcb09046ecea5..3f0cd4dfddaf809d710dd1f53d19dae987105615 100644 (file)
@@ -36,6 +36,19 @@ struct nfs_dns_ent {
 };
 
 
+static void nfs_dns_ent_update(struct cache_head *cnew,
+               struct cache_head *ckey)
+{
+       struct nfs_dns_ent *new;
+       struct nfs_dns_ent *key;
+
+       new = container_of(cnew, struct nfs_dns_ent, h);
+       key = container_of(ckey, struct nfs_dns_ent, h);
+
+       memcpy(&new->addr, &key->addr, key->addrlen);
+       new->addrlen = key->addrlen;
+}
+
 static void nfs_dns_ent_init(struct cache_head *cnew,
                struct cache_head *ckey)
 {
@@ -49,8 +62,7 @@ static void nfs_dns_ent_init(struct cache_head *cnew,
        new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
        if (new->hostname) {
                new->namelen = key->namelen;
-               memcpy(&new->addr, &key->addr, key->addrlen);
-               new->addrlen = key->addrlen;
+               nfs_dns_ent_update(cnew, ckey);
        } else {
                new->namelen = 0;
                new->addrlen = 0;
@@ -234,7 +246,7 @@ static struct cache_detail nfs_dns_resolve = {
        .cache_show = nfs_dns_show,
        .match = nfs_dns_match,
        .init = nfs_dns_ent_init,
-       .update = nfs_dns_ent_init,
+       .update = nfs_dns_ent_update,
        .alloc = nfs_dns_ent_alloc,
 };
 
index 63f2071d6445784899985823cb416dc9861351b7..ae8d02294e462283eb2c421048dc381c8d87bf36 100644 (file)
@@ -123,11 +123,11 @@ nfs_file_open(struct inode *inode, struct file *filp)
                        filp->f_path.dentry->d_parent->d_name.name,
                        filp->f_path.dentry->d_name.name);
 
+       nfs_inc_stats(inode, NFSIOS_VFSOPEN);
        res = nfs_check_flags(filp->f_flags);
        if (res)
                return res;
 
-       nfs_inc_stats(inode, NFSIOS_VFSOPEN);
        res = nfs_open(inode, filp);
        return res;
 }
@@ -237,9 +237,9 @@ nfs_file_flush(struct file *file, fl_owner_t id)
                        dentry->d_parent->d_name.name,
                        dentry->d_name.name);
 
+       nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
        if ((file->f_mode & FMODE_WRITE) == 0)
                return 0;
-       nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
 
        /* Flush writes to the server and return any errors */
        return nfs_do_fsync(ctx, inode);
@@ -262,9 +262,11 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
                (unsigned long) count, (unsigned long) pos);
 
        result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
-       nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, count);
-       if (!result)
+       if (!result) {
                result = generic_file_aio_read(iocb, iov, nr_segs, pos);
+               if (result > 0)
+                       nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, result);
+       }
        return result;
 }
 
@@ -282,8 +284,11 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos,
                (unsigned long) count, (unsigned long long) *ppos);
 
        res = nfs_revalidate_mapping(inode, filp->f_mapping);
-       if (!res)
+       if (!res) {
                res = generic_file_splice_read(filp, ppos, pipe, count, flags);
+               if (res > 0)
+                       nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, res);
+       }
        return res;
 }
 
@@ -596,6 +601,7 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
 {
        struct dentry * dentry = iocb->ki_filp->f_path.dentry;
        struct inode * inode = dentry->d_inode;
+       unsigned long written = 0;
        ssize_t result;
        size_t count = iov_length(iov, nr_segs);
 
@@ -622,14 +628,18 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
        if (!count)
                goto out;
 
-       nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count);
        result = generic_file_aio_write(iocb, iov, nr_segs, pos);
+       if (result > 0)
+               written = result;
+
        /* Return error values for O_DSYNC and IS_SYNC() */
        if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) {
                int err = nfs_do_fsync(nfs_file_open_context(iocb->ki_filp), inode);
                if (err < 0)
                        result = err;
        }
+       if (result > 0)
+               nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
 out:
        return result;
 
@@ -644,6 +654,7 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
 {
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
+       unsigned long written = 0;
        ssize_t ret;
 
        dprintk("NFS splice_write(%s/%s, %lu@%llu)\n",
@@ -654,14 +665,17 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
         * The combination of splice and an O_APPEND destination is disallowed.
         */
 
-       nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count);
-
        ret = generic_file_splice_write(pipe, filp, ppos, count, flags);
+       if (ret > 0)
+               written = ret;
+
        if (ret >= 0 && nfs_need_sync_write(filp, inode)) {
                int err = nfs_do_fsync(nfs_file_open_context(filp), inode);
                if (err < 0)
                        ret = err;
        }
+       if (ret > 0)
+               nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
        return ret;
 }
 
index f141bde7756af861f1853a04bb919a78e377fe81..657201acda84ba2232851971b098787eb1a0b2f4 100644 (file)
@@ -97,22 +97,6 @@ u64 nfs_compat_user_ino64(u64 fileid)
        return ino;
 }
 
-int nfs_write_inode(struct inode *inode, int sync)
-{
-       int ret;
-
-       if (sync) {
-               ret = filemap_fdatawait(inode->i_mapping);
-               if (ret == 0)
-                       ret = nfs_commit_inode(inode, FLUSH_SYNC);
-       } else
-               ret = nfs_commit_inode(inode, 0);
-       if (ret >= 0)
-               return 0;
-       __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
-       return ret;
-}
-
 void nfs_clear_inode(struct inode *inode)
 {
        /*
@@ -130,16 +114,12 @@ void nfs_clear_inode(struct inode *inode)
  */
 int nfs_sync_mapping(struct address_space *mapping)
 {
-       int ret;
+       int ret = 0;
 
-       if (mapping->nrpages == 0)
-               return 0;
-       unmap_mapping_range(mapping, 0, 0, 0);
-       ret = filemap_write_and_wait(mapping);
-       if (ret != 0)
-               goto out;
-       ret = nfs_wb_all(mapping->host);
-out:
+       if (mapping->nrpages != 0) {
+               unmap_mapping_range(mapping, 0, 0, 0);
+               ret = nfs_wb_all(mapping->host);
+       }
        return ret;
 }
 
@@ -511,17 +491,11 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
        int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
        int err;
 
-       /*
-        * Flush out writes to the server in order to update c/mtime.
-        *
-        * Hold the i_mutex to suspend application writes temporarily;
-        * this prevents long-running writing applications from blocking
-        * nfs_wb_nocommit.
-        */
+       /* Flush out writes to the server in order to update c/mtime.  */
        if (S_ISREG(inode->i_mode)) {
-               mutex_lock(&inode->i_mutex);
-               nfs_wb_nocommit(inode);
-               mutex_unlock(&inode->i_mutex);
+               err = filemap_write_and_wait(inode->i_mapping);
+               if (err)
+                       goto out;
        }
 
        /*
@@ -545,6 +519,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
                generic_fillattr(inode, stat);
                stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
        }
+out:
        return err;
 }
 
@@ -574,14 +549,14 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
        nfs_revalidate_inode(server, inode);
 }
 
-static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred)
+static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred)
 {
        struct nfs_open_context *ctx;
 
        ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
        if (ctx != NULL) {
-               ctx->path.dentry = dget(dentry);
-               ctx->path.mnt = mntget(mnt);
+               ctx->path = *path;
+               path_get(&ctx->path);
                ctx->cred = get_rpccred(cred);
                ctx->state = NULL;
                ctx->lockowner = current->files;
@@ -620,11 +595,6 @@ void put_nfs_open_context(struct nfs_open_context *ctx)
        __put_nfs_open_context(ctx, 0);
 }
 
-static void put_nfs_open_context_sync(struct nfs_open_context *ctx)
-{
-       __put_nfs_open_context(ctx, 1);
-}
-
 /*
  * Ensure that mmap has a recent RPC credential for use when writing out
  * shared pages
@@ -671,7 +641,7 @@ static void nfs_file_clear_open_context(struct file *filp)
                spin_lock(&inode->i_lock);
                list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
                spin_unlock(&inode->i_lock);
-               put_nfs_open_context_sync(ctx);
+               __put_nfs_open_context(ctx, filp->f_flags & O_DIRECT ? 0 : 1);
        }
 }
 
@@ -686,7 +656,7 @@ int nfs_open(struct inode *inode, struct file *filp)
        cred = rpc_lookup_cred();
        if (IS_ERR(cred))
                return PTR_ERR(cred);
-       ctx = alloc_nfs_open_context(filp->f_path.mnt, filp->f_path.dentry, cred);
+       ctx = alloc_nfs_open_context(&filp->f_path, cred);
        put_rpccred(cred);
        if (ctx == NULL)
                return -ENOMEM;
@@ -779,7 +749,7 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
        return __nfs_revalidate_inode(server, inode);
 }
 
-static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_space *mapping)
+static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        
@@ -800,49 +770,10 @@ static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_spa
        return 0;
 }
 
-static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
-{
-       int ret = 0;
-
-       mutex_lock(&inode->i_mutex);
-       if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_DATA) {
-               ret = nfs_sync_mapping(mapping);
-               if (ret == 0)
-                       ret = nfs_invalidate_mapping_nolock(inode, mapping);
-       }
-       mutex_unlock(&inode->i_mutex);
-       return ret;
-}
-
-/**
- * nfs_revalidate_mapping_nolock - Revalidate the pagecache
- * @inode - pointer to host inode
- * @mapping - pointer to mapping
- */
-int nfs_revalidate_mapping_nolock(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_timeout(inode) || NFS_STALE(inode)) {
-               ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
-               if (ret < 0)
-                       goto out;
-       }
-       if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
-               ret = nfs_invalidate_mapping_nolock(inode, mapping);
-out:
-       return ret;
-}
-
 /**
  * nfs_revalidate_mapping - Revalidate the pagecache
  * @inode - pointer to host inode
  * @mapping - pointer to mapping
- *
- * This version of the function will take the inode->i_mutex and attempt to
- * flush out all dirty data if it needs to invalidate the page cache.
  */
 int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
@@ -1420,6 +1351,7 @@ static void init_once(void *foo)
        INIT_LIST_HEAD(&nfsi->access_cache_inode_lru);
        INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
        nfsi->npages = 0;
+       nfsi->ncommit = 0;
        atomic_set(&nfsi->silly_count, 1);
        INIT_HLIST_HEAD(&nfsi->silly_list);
        init_waitqueue_head(&nfsi->waitqueue);
index 29e464d23b32fe4107aea88394d0c734e02c10df..11f82f03c5de616125b60f8ea94c941d29240dcb 100644 (file)
@@ -211,7 +211,7 @@ extern int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask);
 extern struct workqueue_struct *nfsiod_workqueue;
 extern struct inode *nfs_alloc_inode(struct super_block *sb);
 extern void nfs_destroy_inode(struct inode *);
-extern int nfs_write_inode(struct inode *,int);
+extern int nfs_write_inode(struct inode *, struct writeback_control *);
 extern void nfs_clear_inode(struct inode *);
 #ifdef CONFIG_NFS_V4
 extern void nfs4_clear_inode(struct inode *);
index 3f8881d1a0504253de64420a9a25f7d7308d73be..24992f0a29f22b12c753004c065f23f34d19df36 100644 (file)
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
-/* A wrapper to handle the EJUKEBOX error message */
+/* A wrapper to handle the EJUKEBOX and EKEYEXPIRED error messages */
 static int
 nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
 {
        int res;
        do {
                res = rpc_call_sync(clnt, msg, flags);
-               if (res != -EJUKEBOX)
+               if (res != -EJUKEBOX && res != -EKEYEXPIRED)
                        break;
                schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
                res = -ERESTARTSYS;
@@ -42,9 +42,10 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
 static int
 nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode)
 {
-       if (task->tk_status != -EJUKEBOX)
+       if (task->tk_status != -EJUKEBOX && task->tk_status != -EKEYEXPIRED)
                return 0;
-       nfs_inc_stats(inode, NFSIOS_DELAY);
+       if (task->tk_status == -EJUKEBOX)
+               nfs_inc_stats(inode, NFSIOS_DELAY);
        task->tk_status = 0;
        rpc_restart_call(task);
        rpc_delay(task, NFS_JUKEBOX_RETRY_TIME);
index 0c6fda33d66ec170a6ecd00a03ba600168b34e70..a187200a7aac254414d2ba7ad94277e712a24742 100644 (file)
@@ -46,6 +46,7 @@ enum nfs4_client_state {
        NFS4CLNT_DELEGRETURN,
        NFS4CLNT_SESSION_RESET,
        NFS4CLNT_SESSION_DRAINING,
+       NFS4CLNT_RECALL_SLOT,
 };
 
 /*
@@ -280,6 +281,7 @@ extern void nfs4_schedule_state_manager(struct nfs_client *);
 extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state);
 extern int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state);
 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 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_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
index 375f0fae2c6a9a4b9536ad416fdc011661ffa28b..eda74c42d552a044b5ba3b8541b9034cc29afff8 100644 (file)
@@ -281,6 +281,7 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
                        }
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
+               case -EKEYEXPIRED:
                        ret = nfs4_delay(server->client, &exception->timeout);
                        if (ret != 0)
                                break;
@@ -418,7 +419,8 @@ static void nfs41_sequence_done(struct nfs_client *clp,
                        clp->cl_last_renewal = timestamp;
                spin_unlock(&clp->cl_lock);
                /* Check sequence flags */
-               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
+               if (atomic_read(&clp->cl_count) > 1)
+                       nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
        }
 out:
        /* The session may be reset by one of the error handlers. */
@@ -724,8 +726,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
        p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
        if (p->o_arg.seqid == NULL)
                goto err_free;
-       p->path.mnt = mntget(path->mnt);
-       p->path.dentry = dget(path->dentry);
+       path_get(path);
+       p->path = *path;
        p->dir = parent;
        p->owner = sp;
        atomic_inc(&sp->so_count);
@@ -1163,7 +1165,7 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
        int err;
        do {
                err = _nfs4_do_open_reclaim(ctx, state);
-               if (err != -NFS4ERR_DELAY)
+               if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED)
                        break;
                nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
@@ -1582,6 +1584,7 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
                        goto out;
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
+               case -EKEYEXPIRED:
                        nfs4_handle_exception(server, err, &exception);
                        err = 0;
                }
@@ -1944,8 +1947,8 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
        calldata->res.seqid = calldata->arg.seqid;
        calldata->res.server = server;
        calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
-       calldata->path.mnt = mntget(path->mnt);
-       calldata->path.dentry = dget(path->dentry);
+       path_get(path);
+       calldata->path = *path;
 
        msg.rpc_argp = &calldata->arg,
        msg.rpc_resp = &calldata->res,
@@ -3145,10 +3148,19 @@ static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_messa
  * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special
  * standalone procedure for queueing an asynchronous RENEW.
  */
+static void nfs4_renew_release(void *data)
+{
+       struct nfs_client *clp = data;
+
+       if (atomic_read(&clp->cl_count) > 1)
+               nfs4_schedule_state_renewal(clp);
+       nfs_put_client(clp);
+}
+
 static void nfs4_renew_done(struct rpc_task *task, void *data)
 {
-       struct nfs_client *clp = (struct nfs_client *)task->tk_msg.rpc_argp;
-       unsigned long timestamp = (unsigned long)data;
+       struct nfs_client *clp = data;
+       unsigned long timestamp = task->tk_start;
 
        if (task->tk_status < 0) {
                /* Unless we're shutting down, schedule state recovery! */
@@ -3164,6 +3176,7 @@ static void nfs4_renew_done(struct rpc_task *task, void *data)
 
 static const struct rpc_call_ops nfs4_renew_ops = {
        .rpc_call_done = nfs4_renew_done,
+       .rpc_release = nfs4_renew_release,
 };
 
 int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred)
@@ -3174,8 +3187,10 @@ int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred)
                .rpc_cred       = cred,
        };
 
+       if (!atomic_inc_not_zero(&clp->cl_count))
+               return -EIO;
        return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
-                       &nfs4_renew_ops, (void *)jiffies);
+                       &nfs4_renew_ops, clp);
 }
 
 int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
@@ -3452,6 +3467,7 @@ _nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                        if (server)
                                nfs_inc_server_stats(server, NFSIOS_DELAY);
                case -NFS4ERR_GRACE:
+               case -EKEYEXPIRED:
                        rpc_delay(task, NFS4_POLL_RETRY_MAX);
                        task->tk_status = 0;
                        return -EAGAIN;
@@ -3564,6 +3580,7 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
                        case -NFS4ERR_RESOURCE:
                                /* The IBM lawyers misread another document! */
                        case -NFS4ERR_DELAY:
+                       case -EKEYEXPIRED:
                                err = nfs4_delay(clp->cl_rpcclient, &timeout);
                }
        } while (err == 0);
@@ -4179,7 +4196,7 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
                if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
                        return 0;
                err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
-               if (err != -NFS4ERR_DELAY)
+               if (err != -NFS4ERR_DELAY && err != -EKEYEXPIRED)
                        break;
                nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
@@ -4204,6 +4221,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
                        goto out;
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
+               case -EKEYEXPIRED:
                        nfs4_handle_exception(server, err, &exception);
                        err = 0;
                }
@@ -4355,6 +4373,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
                                err = 0;
                                goto out;
                        case -NFS4ERR_DELAY:
+                       case -EKEYEXPIRED:
                                break;
                }
                err = nfs4_handle_exception(server, err, &exception);
@@ -4500,7 +4519,7 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
 
                status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
 
-               if (status != NFS4ERR_CLID_INUSE)
+               if (status != -NFS4ERR_CLID_INUSE)
                        break;
 
                if (signalled())
@@ -4554,6 +4573,7 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
        switch (task->tk_status) {
        case -NFS4ERR_DELAY:
        case -NFS4ERR_GRACE:
+       case -EKEYEXPIRED:
                dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
                rpc_delay(task, NFS4_POLL_RETRY_MIN);
                task->tk_status = 0;
@@ -4611,26 +4631,32 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
 /*
  * Reset a slot table
  */
-static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, int max_slots,
-               int old_max_slots, int ivalue)
+static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs,
+                                int ivalue)
 {
+       struct nfs4_slot *new = NULL;
        int i;
        int ret = 0;
 
-       dprintk("--> %s: max_reqs=%u, tbl %p\n", __func__, max_slots, tbl);
+       dprintk("--> %s: max_reqs=%u, tbl->max_slots %d\n", __func__,
+               max_reqs, tbl->max_slots);
 
-       /*
-        * Until we have dynamic slot table adjustment, insist
-        * upon the same slot table size
-        */
-       if (max_slots != old_max_slots) {
-               dprintk("%s reset slot table does't match old\n",
-                       __func__);
-               ret = -EINVAL; /*XXX NFS4ERR_REQ_TOO_BIG ? */
-               goto out;
+       /* Does the newly negotiated max_reqs match the existing slot table? */
+       if (max_reqs != tbl->max_slots) {
+               ret = -ENOMEM;
+               new = kmalloc(max_reqs * sizeof(struct nfs4_slot),
+                             GFP_KERNEL);
+               if (!new)
+                       goto out;
+               ret = 0;
+               kfree(tbl->slots);
        }
        spin_lock(&tbl->slot_tbl_lock);
-       for (i = 0; i < max_slots; ++i)
+       if (new) {
+               tbl->slots = new;
+               tbl->max_slots = max_reqs;
+       }
+       for (i = 0; i < tbl->max_slots; ++i)
                tbl->slots[i].seq_nr = ivalue;
        spin_unlock(&tbl->slot_tbl_lock);
        dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
@@ -4648,16 +4674,12 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session)
        int status;
 
        status = nfs4_reset_slot_table(&session->fc_slot_table,
-                       session->fc_attrs.max_reqs,
-                       session->fc_slot_table.max_slots,
-                       1);
+                       session->fc_attrs.max_reqs, 1);
        if (status)
                return status;
 
        status = nfs4_reset_slot_table(&session->bc_slot_table,
-                       session->bc_attrs.max_reqs,
-                       session->bc_slot_table.max_slots,
-                       0);
+                       session->bc_attrs.max_reqs, 0);
        return status;
 }
 
@@ -4798,16 +4820,14 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
        args->fc_attrs.headerpadsz = 0;
        args->fc_attrs.max_rqst_sz = mxrqst_sz;
        args->fc_attrs.max_resp_sz = mxresp_sz;
-       args->fc_attrs.max_resp_sz_cached = mxresp_sz;
        args->fc_attrs.max_ops = NFS4_MAX_OPS;
        args->fc_attrs.max_reqs = session->clp->cl_rpcclient->cl_xprt->max_reqs;
 
        dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u "
-               "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
+               "max_ops=%u max_reqs=%u\n",
                __func__,
                args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz,
-               args->fc_attrs.max_resp_sz_cached, args->fc_attrs.max_ops,
-               args->fc_attrs.max_reqs);
+               args->fc_attrs.max_ops, args->fc_attrs.max_reqs);
 
        /* Back channel attributes */
        args->bc_attrs.headerpadsz = 0;
@@ -5016,7 +5036,16 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
                                       &res, args.sa_cache_this, 1);
 }
 
-void nfs41_sequence_call_done(struct rpc_task *task, void *data)
+static void nfs41_sequence_release(void *data)
+{
+       struct nfs_client *clp = (struct nfs_client *)data;
+
+       if (atomic_read(&clp->cl_count) > 1)
+               nfs4_schedule_state_renewal(clp);
+       nfs_put_client(clp);
+}
+
+static void nfs41_sequence_call_done(struct rpc_task *task, void *data)
 {
        struct nfs_client *clp = (struct nfs_client *)data;
 
@@ -5024,6 +5053,8 @@ void nfs41_sequence_call_done(struct rpc_task *task, void *data)
 
        if (task->tk_status < 0) {
                dprintk("%s ERROR %d\n", __func__, task->tk_status);
+               if (atomic_read(&clp->cl_count) == 1)
+                       goto out;
 
                if (_nfs4_async_handle_error(task, NULL, clp, NULL)
                                                                == -EAGAIN) {
@@ -5032,7 +5063,7 @@ void nfs41_sequence_call_done(struct rpc_task *task, void *data)
                }
        }
        dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
-
+out:
        kfree(task->tk_msg.rpc_argp);
        kfree(task->tk_msg.rpc_resp);
 
@@ -5057,6 +5088,7 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
 static const struct rpc_call_ops nfs41_sequence_ops = {
        .rpc_call_done = nfs41_sequence_call_done,
        .rpc_call_prepare = nfs41_sequence_prepare,
+       .rpc_release = nfs41_sequence_release,
 };
 
 static int nfs41_proc_async_sequence(struct nfs_client *clp,
@@ -5069,12 +5101,13 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp,
                .rpc_cred = cred,
        };
 
+       if (!atomic_inc_not_zero(&clp->cl_count))
+               return -EIO;
        args = kzalloc(sizeof(*args), GFP_KERNEL);
-       if (!args)
-               return -ENOMEM;
        res = kzalloc(sizeof(*res), GFP_KERNEL);
-       if (!res) {
+       if (!args || !res) {
                kfree(args);
+               nfs_put_client(clp);
                return -ENOMEM;
        }
        res->sr_slotid = NFS4_MAX_SLOT_TABLE;
index 0156c01c212c26ee9cf358a2ec21ed31853f33af..d87f10327b72a8bdd8ea0e23f4050685b2f12a7a 100644 (file)
  * as an rpc_task, not a real kernel thread, so it always runs in rpciod's
  * context.  There is one renewd per nfs_server.
  *
- * TODO: If the send queue gets backlogged (e.g., if the server goes down),
- * we will keep filling the queue with periodic RENEW requests.  We need a
- * mechanism for ensuring that if renewd successfully sends off a request,
- * then it only wakes up when the request is finished.  Maybe use the
- * child task framework of the RPC layer?
  */
 
 #include <linux/mm.h>
@@ -63,7 +58,7 @@ nfs4_renew_state(struct work_struct *work)
        struct nfs_client *clp =
                container_of(work, struct nfs_client, cl_renewd.work);
        struct rpc_cred *cred;
-       long lease, timeout;
+       long lease;
        unsigned long last, now;
 
        ops = nfs4_state_renewal_ops[clp->cl_minorversion];
@@ -75,7 +70,6 @@ nfs4_renew_state(struct work_struct *work)
        lease = clp->cl_lease_time;
        last = clp->cl_last_renewal;
        now = jiffies;
-       timeout = (2 * lease) / 3 + (long)last - (long)now;
        /* Are we close to a lease timeout? */
        if (time_after(now, last + lease/3)) {
                cred = ops->get_state_renewal_cred_locked(clp);
@@ -90,19 +84,15 @@ nfs4_renew_state(struct work_struct *work)
                        /* Queue an asynchronous RENEW. */
                        ops->sched_state_renewal(clp, cred);
                        put_rpccred(cred);
+                       goto out_exp;
                }
-               timeout = (2 * lease) / 3;
-               spin_lock(&clp->cl_lock);
-       } else
+       } else {
                dprintk("%s: failed to call renewd. Reason: lease not expired \n",
                                __func__);
-       if (timeout < 5 * HZ)    /* safeguard */
-               timeout = 5 * HZ;
-       dprintk("%s: requeueing work. Lease period = %ld\n",
-                       __func__, (timeout + HZ - 1) / HZ);
-       cancel_delayed_work(&clp->cl_renewd);
-       schedule_delayed_work(&clp->cl_renewd, timeout);
-       spin_unlock(&clp->cl_lock);
+               spin_unlock(&clp->cl_lock);
+       }
+       nfs4_schedule_state_renewal(clp);
+out_exp:
        nfs_expire_unreferenced_delegations(clp);
 out:
        dprintk("%s: done\n", __func__);
index c1e2733f4fa44d400c53e3e7f2b7fc6f2c30756a..6c5ed51f105eb776b6fc91d6349db9ebd5496380 100644 (file)
@@ -1249,26 +1249,65 @@ static int nfs4_reclaim_lease(struct nfs_client *clp)
 }
 
 #ifdef CONFIG_NFS_V4_1
+void nfs41_handle_recall_slot(struct nfs_client *clp)
+{
+       set_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state);
+       nfs4_schedule_state_recovery(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;
+               nfs4_state_start_reclaim_nograce(clp);
+               nfs4_schedule_state_recovery(clp);
+       }
+}
+
+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);
+               nfs4_schedule_state_recovery(clp);
+       }
+}
+
+static void nfs41_handle_state_revoked(struct nfs_client *clp)
+{
+       /* Temporary */
+       nfs4_reset_all_state(clp);
+}
+
+static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)
+{
+       /* This will need to handle layouts too */
+       nfs_expire_all_delegations(clp);
+}
+
+static void nfs41_handle_cb_path_down(struct nfs_client *clp)
+{
+       nfs_expire_all_delegations(clp);
+       if (test_and_set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) == 0)
+               nfs4_schedule_state_recovery(clp);
+}
+
 void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
 {
        if (!flags)
                return;
-       else if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED) {
-               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
-               nfs4_state_start_reclaim_reboot(clp);
-               nfs4_schedule_state_recovery(clp);
-       } else if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
+       else if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
+               nfs41_handle_server_reboot(clp);
+       else if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
                            SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
                            SEQ4_STATUS_ADMIN_STATE_REVOKED |
-                           SEQ4_STATUS_RECALLABLE_STATE_REVOKED |
-                           SEQ4_STATUS_LEASE_MOVED)) {
-               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
-               nfs4_state_start_reclaim_nograce(clp);
-               nfs4_schedule_state_recovery(clp);
-       } else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
+                           SEQ4_STATUS_LEASE_MOVED))
+               nfs41_handle_state_revoked(clp);
+       else if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
+               nfs41_handle_recallable_state_revoked(clp);
+       else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
                            SEQ4_STATUS_BACKCHANNEL_FAULT |
                            SEQ4_STATUS_CB_PATH_DOWN_SESSION))
-               nfs_expire_all_delegations(clp);
+               nfs41_handle_cb_path_down(clp);
 }
 
 static int nfs4_reset_session(struct nfs_client *clp)
@@ -1285,23 +1324,52 @@ 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);
-       if (status)
+       if (status) {
                status = nfs4_recovery_handle_error(clp, status);
+               goto out;
+       }
+       /* create_session negotiated new slot table */
+       clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state);
 
-out:
-       /*
-        * Let the state manager reestablish state
-        */
-       if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
-           status == 0)
+        /* Let the state manager reestablish state */
+       if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                nfs41_setup_state_renewal(clp);
-
+out:
        return status;
 }
 
+static int nfs4_recall_slot(struct nfs_client *clp)
+{
+       struct nfs4_slot_table *fc_tbl = &clp->cl_session->fc_slot_table;
+       struct nfs4_channel_attrs *fc_attrs = &clp->cl_session->fc_attrs;
+       struct nfs4_slot *new, *old;
+       int i;
+
+       nfs4_begin_drain_session(clp);
+       new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot),
+                     GFP_KERNEL);
+        if (!new)
+               return -ENOMEM;
+
+       spin_lock(&fc_tbl->slot_tbl_lock);
+       for (i = 0; i < fc_tbl->target_max_slots; i++)
+               new[i].seq_nr = fc_tbl->slots[i].seq_nr;
+       old = fc_tbl->slots;
+       fc_tbl->slots = new;
+       fc_tbl->max_slots = fc_tbl->target_max_slots;
+       fc_tbl->target_max_slots = 0;
+       fc_attrs->max_reqs = fc_tbl->max_slots;
+       spin_unlock(&fc_tbl->slot_tbl_lock);
+
+       kfree(old);
+       nfs4_end_drain_session(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
@@ -1314,6 +1382,7 @@ static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
                case -NFS4ERR_DELAY:
                case -NFS4ERR_CLID_INUSE:
                case -EAGAIN:
+               case -EKEYEXPIRED:
                        break;
 
                case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
@@ -1397,6 +1466,15 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        nfs_client_return_marked_delegations(clp);
                        continue;
                }
+               /* Recall session slots */
+               if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)
+                  && nfs4_has_session(clp)) {
+                       status = nfs4_recall_slot(clp);
+                       if (status < 0)
+                               goto out_error;
+                       continue;
+               }
+
 
                nfs4_clear_state_manager_bit(clp);
                /* Did we race with an attempt to give us more work? */
index 5cd5184b56dbad12a8e204ecd22adcb94c3dadf1..4d338be492cb28f6e91ae9eecfe30af64555154d 100644 (file)
@@ -1578,6 +1578,14 @@ 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;
+       u32 max_resp_sz_cached;
+
+       /*
+        * Assumes OPEN is the biggest non-idempotent compound.
+        * 2 is the verifier.
+        */
+       max_resp_sz_cached = (NFS4_dec_open_sz + RPC_REPHDRSIZE +
+                             RPC_MAX_AUTH_SIZE + 2) * XDR_UNIT;
 
        len = scnprintf(machine_name, sizeof(machine_name), "%s",
                        clp->cl_ipaddr);
@@ -1592,7 +1600,7 @@ static void encode_create_session(struct xdr_stream *xdr,
        *p++ = cpu_to_be32(args->fc_attrs.headerpadsz); /* header padding size */
        *p++ = cpu_to_be32(args->fc_attrs.max_rqst_sz); /* max req size */
        *p++ = cpu_to_be32(args->fc_attrs.max_resp_sz); /* max resp size */
-       *p++ = cpu_to_be32(args->fc_attrs.max_resp_sz_cached);  /* Max resp sz cached */
+       *p++ = cpu_to_be32(max_resp_sz_cached);         /* Max resp sz cached */
        *p++ = cpu_to_be32(args->fc_attrs.max_ops);     /* max operations */
        *p++ = cpu_to_be32(args->fc_attrs.max_reqs);    /* max requests */
        *p++ = cpu_to_be32(0);                          /* rdmachannel_attrs */
index ef583854d8d03c77864c7d68d7cb3af3c88cfa47..c752d944fe9e93e2cf1759b49e6c671a1caaa7b7 100644 (file)
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
+/*
+ * wrapper to handle the -EKEYEXPIRED error message. This should generally
+ * only happen if using krb5 auth and a user's TGT expires. NFSv2 doesn't
+ * support the NFSERR_JUKEBOX error code, but we handle this situation in the
+ * same way that we handle that error with NFSv3.
+ */
+static int
+nfs_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
+{
+       int res;
+       do {
+               res = rpc_call_sync(clnt, msg, flags);
+               if (res != -EKEYEXPIRED)
+                       break;
+               schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
+               res = -ERESTARTSYS;
+       } while (!fatal_signal_pending(current));
+       return res;
+}
+
+#define rpc_call_sync(clnt, msg, flags)        nfs_rpc_wrapper(clnt, msg, flags)
+
+static int
+nfs_async_handle_expired_key(struct rpc_task *task)
+{
+       if (task->tk_status != -EKEYEXPIRED)
+               return 0;
+       task->tk_status = 0;
+       rpc_restart_call(task);
+       rpc_delay(task, NFS_JUKEBOX_RETRY_TIME);
+       return 1;
+}
+
 /*
  * Bare-bones access to getattr: this is for nfs_read_super.
  */
@@ -307,6 +340,8 @@ nfs_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
 
 static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
 {
+       if (nfs_async_handle_expired_key(task))
+               return 0;
        nfs_mark_for_revalidate(dir);
        return 1;
 }
@@ -560,6 +595,9 @@ 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)
 {
+       if (nfs_async_handle_expired_key(task))
+               return -EAGAIN;
+
        nfs_invalidate_atime(data->inode);
        if (task->tk_status >= 0) {
                nfs_refresh_inode(data->inode, data->res.fattr);
@@ -579,6 +617,9 @@ static void nfs_proc_read_setup(struct nfs_read_data *data, struct rpc_message *
 
 static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
+       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);
        return 0;
index 412738dbfbc7e1024f90887e5e3cdd1a07a34563..2ea9e5c27e5521d10b82d893b316c43c85ccad57 100644 (file)
@@ -50,7 +50,7 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
        struct page *page;
        void *err;
 
-       err = ERR_PTR(nfs_revalidate_mapping_nolock(inode, inode->i_mapping));
+       err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
        if (err)
                goto read_failed;
        page = read_cache_page(&inode->i_data, 0,
index d63d964a0392ed9a73e759b18c34e1ecfe2368c0..53ff70e2399335a408a38c6f779282c1663a2f30 100644 (file)
@@ -438,6 +438,7 @@ nfs_mark_request_commit(struct nfs_page *req)
        radix_tree_tag_set(&nfsi->nfs_page_tree,
                        req->wb_index,
                        NFS_PAGE_TAG_COMMIT);
+       nfsi->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);
@@ -501,57 +502,6 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
 }
 #endif
 
-/*
- * Wait for a request to complete.
- *
- * Interruptible by fatal signals only.
- */
-static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, unsigned int npages)
-{
-       struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_page *req;
-       pgoff_t idx_end, next;
-       unsigned int            res = 0;
-       int                     error;
-
-       if (npages == 0)
-               idx_end = ~0;
-       else
-               idx_end = idx_start + npages - 1;
-
-       next = idx_start;
-       while (radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, (void **)&req, next, 1, NFS_PAGE_TAG_LOCKED)) {
-               if (req->wb_index > idx_end)
-                       break;
-
-               next = req->wb_index + 1;
-               BUG_ON(!NFS_WBACK_BUSY(req));
-
-               kref_get(&req->wb_kref);
-               spin_unlock(&inode->i_lock);
-               error = nfs_wait_on_request(req);
-               nfs_release_request(req);
-               spin_lock(&inode->i_lock);
-               if (error < 0)
-                       return error;
-               res++;
-       }
-       return res;
-}
-
-static void nfs_cancel_commit_list(struct list_head *head)
-{
-       struct nfs_page *req;
-
-       while(!list_empty(head)) {
-               req = nfs_list_entry(head->next);
-               nfs_list_remove_request(req);
-               nfs_clear_request_commit(req);
-               nfs_inode_remove_request(req);
-               nfs_unlock_request(req);
-       }
-}
-
 #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
 static int
 nfs_need_commit(struct nfs_inode *nfsi)
@@ -573,11 +523,17 @@ static int
 nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       int ret;
 
        if (!nfs_need_commit(nfsi))
                return 0;
 
-       return nfs_scan_list(nfsi, dst, idx_start, npages, NFS_PAGE_TAG_COMMIT);
+       ret = nfs_scan_list(nfsi, dst, idx_start, npages, NFS_PAGE_TAG_COMMIT);
+       if (ret > 0)
+               nfsi->ncommit -= ret;
+       if (nfs_need_commit(NFS_I(inode)))
+               __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
+       return ret;
 }
 #else
 static inline int nfs_need_commit(struct nfs_inode *nfsi)
@@ -642,9 +598,10 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
                spin_lock(&inode->i_lock);
        }
 
-       if (nfs_clear_request_commit(req))
-               radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
-                               req->wb_index, NFS_PAGE_TAG_COMMIT);
+       if (nfs_clear_request_commit(req) &&
+                       radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
+                               req->wb_index, NFS_PAGE_TAG_COMMIT) != NULL)
+               NFS_I(inode)->ncommit--;
 
        /* Okay, the request matches. Update the region */
        if (offset < req->wb_offset) {
@@ -1391,7 +1348,7 @@ static const struct rpc_call_ops nfs_commit_ops = {
        .rpc_release = nfs_commit_release,
 };
 
-int nfs_commit_inode(struct inode *inode, int how)
+static int nfs_commit_inode(struct inode *inode, int how)
 {
        LIST_HEAD(head);
        int res;
@@ -1406,92 +1363,51 @@ int nfs_commit_inode(struct inode *inode, int how)
        }
        return res;
 }
-#else
-static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how)
-{
-       return 0;
-}
-#endif
 
-long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how)
+static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_control *wbc)
 {
-       struct inode *inode = mapping->host;
-       pgoff_t idx_start, idx_end;
-       unsigned int npages = 0;
-       LIST_HEAD(head);
-       int nocommit = how & FLUSH_NOCOMMIT;
-       long pages, ret;
-
-       /* FIXME */
-       if (wbc->range_cyclic)
-               idx_start = 0;
-       else {
-               idx_start = wbc->range_start >> PAGE_CACHE_SHIFT;
-               idx_end = wbc->range_end >> PAGE_CACHE_SHIFT;
-               if (idx_end > idx_start) {
-                       pgoff_t l_npages = 1 + idx_end - idx_start;
-                       npages = l_npages;
-                       if (sizeof(npages) != sizeof(l_npages) &&
-                                       (pgoff_t)npages != l_npages)
-                               npages = 0;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       int flags = FLUSH_SYNC;
+       int ret = 0;
+
+       /* Don't commit yet if this is a non-blocking flush and there are
+        * lots of outstanding writes for this mapping.
+        */
+       if (wbc->sync_mode == WB_SYNC_NONE &&
+           nfsi->ncommit <= (nfsi->npages >> 1))
+               goto out_mark_dirty;
+
+       if (wbc->nonblocking || wbc->for_background)
+               flags = 0;
+       ret = nfs_commit_inode(inode, flags);
+       if (ret >= 0) {
+               if (wbc->sync_mode == WB_SYNC_NONE) {
+                       if (ret < wbc->nr_to_write)
+                               wbc->nr_to_write -= ret;
+                       else
+                               wbc->nr_to_write = 0;
                }
+               return 0;
        }
-       how &= ~FLUSH_NOCOMMIT;
-       spin_lock(&inode->i_lock);
-       do {
-               ret = nfs_wait_on_requests_locked(inode, idx_start, npages);
-               if (ret != 0)
-                       continue;
-               if (nocommit)
-                       break;
-               pages = nfs_scan_commit(inode, &head, idx_start, npages);
-               if (pages == 0)
-                       break;
-               if (how & FLUSH_INVALIDATE) {
-                       spin_unlock(&inode->i_lock);
-                       nfs_cancel_commit_list(&head);
-                       ret = pages;
-                       spin_lock(&inode->i_lock);
-                       continue;
-               }
-               pages += nfs_scan_commit(inode, &head, 0, 0);
-               spin_unlock(&inode->i_lock);
-               ret = nfs_commit_list(inode, &head, how);
-               spin_lock(&inode->i_lock);
-
-       } while (ret >= 0);
-       spin_unlock(&inode->i_lock);
+out_mark_dirty:
+       __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
        return ret;
 }
-
-static int __nfs_write_mapping(struct address_space *mapping, struct writeback_control *wbc, int how)
+#else
+static int nfs_commit_inode(struct inode *inode, int how)
 {
-       int ret;
-
-       ret = nfs_writepages(mapping, wbc);
-       if (ret < 0)
-               goto out;
-       ret = nfs_sync_mapping_wait(mapping, wbc, how);
-       if (ret < 0)
-               goto out;
        return 0;
-out:
-       __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
-       return ret;
 }
 
-/* Two pass sync: first using WB_SYNC_NONE, then WB_SYNC_ALL */
-static int nfs_write_mapping(struct address_space *mapping, int how)
+static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_control *wbc)
 {
-       struct writeback_control wbc = {
-               .bdi = mapping->backing_dev_info,
-               .sync_mode = WB_SYNC_ALL,
-               .nr_to_write = LONG_MAX,
-               .range_start = 0,
-               .range_end = LLONG_MAX,
-       };
+       return 0;
+}
+#endif
 
-       return __nfs_write_mapping(mapping, &wbc, how);
+int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       return nfs_commit_unstable_pages(inode, wbc);
 }
 
 /*
@@ -1499,37 +1415,26 @@ static int nfs_write_mapping(struct address_space *mapping, int how)
  */
 int nfs_wb_all(struct inode *inode)
 {
-       return nfs_write_mapping(inode->i_mapping, 0);
-}
+       struct writeback_control wbc = {
+               .sync_mode = WB_SYNC_ALL,
+               .nr_to_write = LONG_MAX,
+               .range_start = 0,
+               .range_end = LLONG_MAX,
+       };
 
-int nfs_wb_nocommit(struct inode *inode)
-{
-       return nfs_write_mapping(inode->i_mapping, FLUSH_NOCOMMIT);
+       return sync_inode(inode, &wbc);
 }
 
 int nfs_wb_page_cancel(struct inode *inode, struct page *page)
 {
        struct nfs_page *req;
-       loff_t range_start = page_offset(page);
-       loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
-       struct writeback_control wbc = {
-               .bdi = page->mapping->backing_dev_info,
-               .sync_mode = WB_SYNC_ALL,
-               .nr_to_write = LONG_MAX,
-               .range_start = range_start,
-               .range_end = range_end,
-       };
        int ret = 0;
 
        BUG_ON(!PageLocked(page));
        for (;;) {
                req = nfs_page_find_request(page);
                if (req == NULL)
-                       goto out;
-               if (test_bit(PG_CLEAN, &req->wb_flags)) {
-                       nfs_release_request(req);
                        break;
-               }
                if (nfs_lock_request_dontget(req)) {
                        nfs_inode_remove_request(req);
                        /*
@@ -1543,54 +1448,54 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
                ret = nfs_wait_on_request(req);
                nfs_release_request(req);
                if (ret < 0)
-                       goto out;
+                       break;
        }
-       if (!PagePrivate(page))
-               return 0;
-       ret = nfs_sync_mapping_wait(page->mapping, &wbc, FLUSH_INVALIDATE);
-out:
        return ret;
 }
 
-static int nfs_wb_page_priority(struct inode *inode, struct page *page,
-                               int how)
+/*
+ * Write back all requests on one page - we do this before reading it.
+ */
+int nfs_wb_page(struct inode *inode, struct page *page)
 {
        loff_t range_start = page_offset(page);
        loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
        struct writeback_control wbc = {
-               .bdi = page->mapping->backing_dev_info,
                .sync_mode = WB_SYNC_ALL,
-               .nr_to_write = LONG_MAX,
+               .nr_to_write = 0,
                .range_start = range_start,
                .range_end = range_end,
        };
+       struct nfs_page *req;
+       int need_commit;
        int ret;
 
-       do {
+       while(PagePrivate(page)) {
                if (clear_page_dirty_for_io(page)) {
                        ret = nfs_writepage_locked(page, &wbc);
                        if (ret < 0)
                                goto out_error;
-               } else if (!PagePrivate(page))
+               }
+               req = nfs_find_and_lock_request(page);
+               if (!req)
                        break;
-               ret = nfs_sync_mapping_wait(page->mapping, &wbc, how);
-               if (ret < 0)
+               if (IS_ERR(req)) {
+                       ret = PTR_ERR(req);
                        goto out_error;
-       } while (PagePrivate(page));
+               }
+               need_commit = test_bit(PG_CLEAN, &req->wb_flags);
+               nfs_clear_page_tag_locked(req);
+               if (need_commit) {
+                       ret = nfs_commit_inode(inode, FLUSH_SYNC);
+                       if (ret < 0)
+                               goto out_error;
+               }
+       }
        return 0;
 out_error:
-       __mark_inode_dirty(inode, I_DIRTY_PAGES);
        return ret;
 }
 
-/*
- * Write back all requests on one page - we do this before reading it.
- */
-int nfs_wb_page(struct inode *inode, struct page* page)
-{
-       return nfs_wb_page_priority(inode, page, FLUSH_STABLE);
-}
-
 #ifdef CONFIG_MIGRATION
 int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
                struct page *page)
index d3854d94b7cf367cad8a04b665f0c18271355368..bf9cbd242dddbe5f7cda1969fc3eeebbd6e023ad 100644 (file)
@@ -36,10 +36,9 @@ static struct file *do_open(char *name, int flags)
                return ERR_PTR(error);
 
        if (flags == O_RDWR)
-               error = may_open(&nd.path, MAY_READ|MAY_WRITE,
-                                          FMODE_READ|FMODE_WRITE);
+               error = may_open(&nd.path, MAY_READ|MAY_WRITE, flags);
        else
-               error = may_open(&nd.path, MAY_WRITE, FMODE_WRITE);
+               error = may_open(&nd.path, MAY_WRITE, flags);
 
        if (!error)
                return dentry_open(nd.path.dentry, nd.path.mnt, flags,
index c6eed2a3b0935f4de0363339878e097094f45024..4bc22c763de7b433b4a37f2affdf75cc560d3908 100644 (file)
@@ -525,6 +525,8 @@ static struct rpc_cred *callback_cred;
 
 int set_callback_cred(void)
 {
+       if (callback_cred)
+               return 0;
        callback_cred = rpc_lookup_machine_cred();
        if (!callback_cred)
                return -ENOMEM;
@@ -542,7 +544,8 @@ void do_probe_callback(struct nfs4_client *clp)
        };
        int status;
 
-       status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_SOFT,
+       status = rpc_call_async(cb->cb_client, &msg,
+                               RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
                                &nfsd4_cb_probe_ops, (void *)clp);
        if (status) {
                warn_no_callback_path(clp, status);
index 5a754f7b71ed34a84717903f018523e2008f31f5..98fb98e330b4004907d899937e13e2f1c32368ee 100644 (file)
@@ -119,9 +119,7 @@ out_no_tfm:
 static void
 nfsd4_sync_rec_dir(void)
 {
-       mutex_lock(&rec_dir.dentry->d_inode->i_mutex);
-       nfsd_sync_dir(rec_dir.dentry);
-       mutex_unlock(&rec_dir.dentry->d_inode->i_mutex);
+       vfs_fsync(NULL, rec_dir.dentry, 0);
 }
 
 int
index f19ed866c95fd18ef5da370b9cd09387c6d71c8a..c97fddbd17db8d15f0ce5a21d1ec6580a1be603c 100644 (file)
@@ -1998,7 +1998,9 @@ nfs4_file_downgrade(struct file *filp, unsigned int share_access)
 {
        if (share_access & NFS4_SHARE_ACCESS_WRITE) {
                drop_file_write_access(filp);
+               spin_lock(&filp->f_lock);
                filp->f_mode = (filp->f_mode | FMODE_READ) & ~FMODE_WRITE;
+               spin_unlock(&filp->f_lock);
        }
 }
 
@@ -2480,8 +2482,10 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
        }
        memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t));
 
-       if (nfsd4_has_session(&resp->cstate))
+       if (nfsd4_has_session(&resp->cstate)) {
                open->op_stateowner->so_confirmed = 1;
+               nfsd4_create_clid_dir(open->op_stateowner->so_client);
+       }
 
        /*
        * Attempt to hand out a delegation. No error return, because the
index a8587e90fd5a837bfce993d9cebaeb9f1697f825..78c7e24e5129b595670dfc13d362af692ae5359f 100644 (file)
@@ -1434,7 +1434,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                }
                op->opnum = ntohl(*argp->p++);
 
-               if (op->opnum >= OP_ACCESS && op->opnum < ops->nops)
+               if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP)
                        op->status = ops->decoders[op->opnum](argp, &op->u);
                else {
                        op->opnum = OP_ILLEGAL;
@@ -2121,9 +2121,15 @@ out_acl:
                 * and this is the root of a cross-mounted filesystem.
                 */
                if (ignore_crossmnt == 0 &&
-                   exp->ex_path.mnt->mnt_root->d_inode == dentry->d_inode) {
-                       err = vfs_getattr(exp->ex_path.mnt->mnt_parent,
-                               exp->ex_path.mnt->mnt_mountpoint, &stat);
+                   dentry == exp->ex_path.mnt->mnt_root) {
+                       struct path path = exp->ex_path;
+                       path_get(&path);
+                       while (follow_up(&path)) {
+                               if (path.dentry != path.mnt->mnt_root)
+                                       break;
+                       }
+                       err = vfs_getattr(path.mnt, path.dentry, &stat);
+                       path_put(&path);
                        if (err)
                                goto out_nfserr;
                }
index 2604c3e70ea547f69e19146afbc11fe769f6ba5a..0f0e77f2012f64951d9b8c1fc54f19156fab6f62 100644 (file)
@@ -988,6 +988,7 @@ static ssize_t __write_ports_delfd(char *buf)
 static ssize_t __write_ports_addxprt(char *buf)
 {
        char transport[16];
+       struct svc_xprt *xprt;
        int port, err;
 
        if (sscanf(buf, "%15s %4u", transport, &port) != 2)
@@ -1002,13 +1003,24 @@ static ssize_t __write_ports_addxprt(char *buf)
 
        err = svc_create_xprt(nfsd_serv, transport,
                                PF_INET, port, SVC_SOCK_ANONYMOUS);
-       if (err < 0) {
-               /* Give a reasonable perror msg for bad transport string */
-               if (err == -ENOENT)
-                       err = -EPROTONOSUPPORT;
-               return err;
-       }
+       if (err < 0)
+               goto out_err;
+
+       err = svc_create_xprt(nfsd_serv, transport,
+                               PF_INET6, port, SVC_SOCK_ANONYMOUS);
+       if (err < 0 && err != -EAFNOSUPPORT)
+               goto out_close;
        return 0;
+out_close:
+       xprt = svc_find_xprt(nfsd_serv, transport, PF_INET, port);
+       if (xprt != NULL) {
+               svc_close_xprt(xprt);
+               svc_xprt_put(xprt);
+       }
+out_err:
+       /* Decrease the count, but don't shut down the service */
+       nfsd_serv->sv_nrthreads--;
+       return err;
 }
 
 /*
index 8715d194561aa626bff1d18991a2ac667be00715..a11b0e8678eeff6585687afe1d0701663e11144a 100644 (file)
 #include <linux/fcntl.h>
 #include <linux/namei.h>
 #include <linux/delay.h>
-#include <linux/quotaops.h>
 #include <linux/fsnotify.h>
 #include <linux/posix_acl_xattr.h>
 #include <linux/xattr.h>
 #include <linux/jhash.h>
 #include <linux/ima.h>
 #include <asm/uaccess.h>
+#include <linux/exportfs.h>
+#include <linux/writeback.h>
 
 #ifdef CONFIG_NFSD_V3
 #include "xdr3.h"
@@ -271,6 +272,32 @@ out:
        return err;
 }
 
+/*
+ * Commit metadata changes to stable storage.
+ */
+static int
+commit_metadata(struct svc_fh *fhp)
+{
+       struct inode *inode = fhp->fh_dentry->d_inode;
+       const struct export_operations *export_ops = inode->i_sb->s_export_op;
+       int error = 0;
+
+       if (!EX_ISSYNC(fhp->fh_export))
+               return 0;
+
+       if (export_ops->commit_metadata) {
+               error = export_ops->commit_metadata(inode);
+       } else {
+               struct writeback_control wbc = {
+                       .sync_mode = WB_SYNC_ALL,
+                       .nr_to_write = 0, /* metadata only */
+               };
+
+               error = sync_inode(inode, &wbc);
+       }
+
+       return error;
+}
 
 /*
  * Set various file attributes.
@@ -361,7 +388,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                 * If we are changing the size of the file, then
                 * we need to break all leases.
                 */
-               host_err = break_lease(inode, FMODE_WRITE | O_NONBLOCK);
+               host_err = break_lease(inode, O_WRONLY | O_NONBLOCK);
                if (host_err == -EWOULDBLOCK)
                        host_err = -ETIMEDOUT;
                if (host_err) /* ENOMEM or EWOULDBLOCK */
@@ -377,7 +404,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                        put_write_access(inode);
                        goto out_nfserr;
                }
-               vfs_dq_init(inode);
        }
 
        /* sanitize the mode change */
@@ -734,7 +760,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
         * Check to see if there are any leases on this file.
         * This may block while leases are broken.
         */
-       host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? FMODE_WRITE : 0));
+       host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0));
        if (host_err == -EWOULDBLOCK)
                host_err = -ETIMEDOUT;
        if (host_err) /* NOMEM or WOULDBLOCK */
@@ -745,8 +771,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                        flags = O_RDWR|O_LARGEFILE;
                else
                        flags = O_WRONLY|O_LARGEFILE;
-
-               vfs_dq_init(inode);
        }
        *filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_path.mnt),
                            flags, current_cred());
@@ -770,43 +794,6 @@ nfsd_close(struct file *filp)
        fput(filp);
 }
 
-/*
- * Sync a file
- * As this calls fsync (not fdatasync) there is no need for a write_inode
- * after it.
- */
-static inline int nfsd_dosync(struct file *filp, struct dentry *dp,
-                             const struct file_operations *fop)
-{
-       struct inode *inode = dp->d_inode;
-       int (*fsync) (struct file *, struct dentry *, int);
-       int err;
-
-       err = filemap_write_and_wait(inode->i_mapping);
-       if (err == 0 && fop && (fsync = fop->fsync))
-               err = fsync(filp, dp, 0);
-       return err;
-}
-
-static int
-nfsd_sync(struct file *filp)
-{
-        int err;
-       struct inode *inode = filp->f_path.dentry->d_inode;
-       dprintk("nfsd: sync file %s\n", filp->f_path.dentry->d_name.name);
-       mutex_lock(&inode->i_mutex);
-       err=nfsd_dosync(filp, filp->f_path.dentry, filp->f_op);
-       mutex_unlock(&inode->i_mutex);
-
-       return err;
-}
-
-int
-nfsd_sync_dir(struct dentry *dp)
-{
-       return nfsd_dosync(NULL, dp, dp->d_inode->i_fop);
-}
-
 /*
  * Obtain the readahead parameters for the file
  * specified by (dev, ino).
@@ -1010,7 +997,7 @@ static int wait_for_concurrent_writes(struct file *file)
 
        if (inode->i_state & I_DIRTY) {
                dprintk("nfsd: write sync %d\n", task_pid_nr(current));
-               err = nfsd_sync(file);
+               err = vfs_fsync(file, file->f_path.dentry, 0);
        }
        last_ino = inode->i_ino;
        last_dev = inode->i_sb->s_dev;
@@ -1158,8 +1145,9 @@ out:
 #ifdef CONFIG_NFSD_V3
 /*
  * Commit all pending writes to stable storage.
- * Strictly speaking, we could sync just the indicated file region here,
- * but there's currently no way we can ask the VFS to do so.
+ *
+ * Note: we only guarantee that data that lies within the range specified
+ * by the 'offset' and 'count' parameters will be synced.
  *
  * Unfortunately we cannot lock the file to make sure we return full WCC
  * data to the client, as locking happens lower down in the filesystem.
@@ -1169,23 +1157,32 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                loff_t offset, unsigned long count)
 {
        struct file     *file;
-       __be32          err;
+       loff_t          end = LLONG_MAX;
+       __be32          err = nfserr_inval;
 
-       if ((u64)count > ~(u64)offset)
-               return nfserr_inval;
+       if (offset < 0)
+               goto out;
+       if (count != 0) {
+               end = offset + (loff_t)count - 1;
+               if (end < offset)
+                       goto out;
+       }
 
        err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file);
        if (err)
-               return err;
+               goto out;
        if (EX_ISSYNC(fhp->fh_export)) {
-               if (file->f_op && file->f_op->fsync) {
-                       err = nfserrno(nfsd_sync(file));
-               } else {
+               int err2 = vfs_fsync_range(file, file->f_path.dentry,
+                               offset, end, 0);
+
+               if (err2 != -EINVAL)
+                       err = nfserrno(err2);
+               else
                        err = nfserr_notsupp;
-               }
        }
 
        nfsd_close(file);
+out:
        return err;
 }
 #endif /* CONFIG_NFSD_V3 */
@@ -1338,12 +1335,14 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                goto out_nfserr;
        }
 
-       if (EX_ISSYNC(fhp->fh_export)) {
-               err = nfserrno(nfsd_sync_dir(dentry));
-               write_inode_now(dchild->d_inode, 1);
-       }
+       err = nfsd_create_setattr(rqstp, resfhp, iap);
 
-       err2 = nfsd_create_setattr(rqstp, resfhp, iap);
+       /*
+        * nfsd_setattr already committed the child.  Transactional filesystems
+        * had a chance to commit changes for both parent and child
+        * simultaneously making the following commit_metadata a noop.
+        */
+       err2 = nfserrno(commit_metadata(fhp));
        if (err2)
                err = err2;
        mnt_drop_write(fhp->fh_export->ex_path.mnt);
@@ -1375,7 +1374,6 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
        struct dentry   *dentry, *dchild = NULL;
        struct inode    *dirp;
        __be32          err;
-       __be32          err2;
        int             host_err;
        __u32           v_mtime=0, v_atime=0;
 
@@ -1470,11 +1468,6 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (created)
                *created = 1;
 
-       if (EX_ISSYNC(fhp->fh_export)) {
-               err = nfserrno(nfsd_sync_dir(dentry));
-               /* setattr will sync the child (or not) */
-       }
-
        nfsd_check_ignore_resizing(iap);
 
        if (createmode == NFS3_CREATE_EXCLUSIVE) {
@@ -1489,9 +1482,13 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
        }
 
  set_attr:
-       err2 = nfsd_create_setattr(rqstp, resfhp, iap);
-       if (err2)
-               err = err2;
+       err = nfsd_create_setattr(rqstp, resfhp, iap);
+
+       /*
+        * nfsd_setattr already committed the child (and possibly also the parent).
+        */
+       if (!err)
+               err = nfserrno(commit_metadata(fhp));
 
        mnt_drop_write(fhp->fh_export->ex_path.mnt);
        /*
@@ -1606,12 +1603,9 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
                }
        } else
                host_err = vfs_symlink(dentry->d_inode, dnew, path);
-
-       if (!host_err) {
-               if (EX_ISSYNC(fhp->fh_export))
-                       host_err = nfsd_sync_dir(dentry);
-       }
        err = nfserrno(host_err);
+       if (!err)
+               err = nfserrno(commit_metadata(fhp));
        fh_unlock(fhp);
 
        mnt_drop_write(fhp->fh_export->ex_path.mnt);
@@ -1673,11 +1667,9 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
        }
        host_err = vfs_link(dold, dirp, dnew);
        if (!host_err) {
-               if (EX_ISSYNC(ffhp->fh_export)) {
-                       err = nfserrno(nfsd_sync_dir(ddir));
-                       write_inode_now(dest, 1);
-               }
-               err = 0;
+               err = nfserrno(commit_metadata(ffhp));
+               if (!err)
+                       err = nfserrno(commit_metadata(tfhp));
        } else {
                if (host_err == -EXDEV && rqstp->rq_vers == 2)
                        err = nfserr_acces;
@@ -1773,10 +1765,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
                goto out_dput_new;
 
        host_err = vfs_rename(fdir, odentry, tdir, ndentry);
-       if (!host_err && EX_ISSYNC(tfhp->fh_export)) {
-               host_err = nfsd_sync_dir(tdentry);
+       if (!host_err) {
+               host_err = commit_metadata(tfhp);
                if (!host_err)
-                       host_err = nfsd_sync_dir(fdentry);
+                       host_err = commit_metadata(ffhp);
        }
 
        mnt_drop_write(ffhp->fh_export->ex_path.mnt);
@@ -1857,12 +1849,9 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
 
        dput(rdentry);
 
-       if (host_err)
-               goto out_drop;
-       if (EX_ISSYNC(fhp->fh_export))
-               host_err = nfsd_sync_dir(dentry);
+       if (!host_err)
+               host_err = commit_metadata(fhp);
 
-out_drop:
        mnt_drop_write(fhp->fh_export->ex_path.mnt);
 out_nfserr:
        err = nfserrno(host_err);
index 76d803e060a95d983b633552a7a75c2b1bf42368..0092840492eee910ee80518072c0f754044b40be 100644 (file)
@@ -224,7 +224,7 @@ fail:
  * len <= NILFS_NAME_LEN and de != NULL are guaranteed by caller.
  */
 static int
-nilfs_match(int len, const char * const name, struct nilfs_dir_entry *de)
+nilfs_match(int len, const unsigned char *name, struct nilfs_dir_entry *de)
 {
        if (len != de->name_len)
                return 0;
@@ -349,11 +349,11 @@ done:
  * Entry is guaranteed to be valid.
  */
 struct nilfs_dir_entry *
-nilfs_find_entry(struct inode *dir, struct dentry *dentry,
+nilfs_find_entry(struct inode *dir, const struct qstr *qstr,
                 struct page **res_page)
 {
-       const char *name = dentry->d_name.name;
-       int namelen = dentry->d_name.len;
+       const unsigned char *name = qstr->name;
+       int namelen = qstr->len;
        unsigned reclen = NILFS_DIR_REC_LEN(namelen);
        unsigned long start, n;
        unsigned long npages = dir_pages(dir);
@@ -424,13 +424,13 @@ struct nilfs_dir_entry *nilfs_dotdot(struct inode *dir, struct page **p)
        return de;
 }
 
-ino_t nilfs_inode_by_name(struct inode *dir, struct dentry *dentry)
+ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr)
 {
        ino_t res = 0;
        struct nilfs_dir_entry *de;
        struct page *page;
 
-       de = nilfs_find_entry(dir, dentry, &page);
+       de = nilfs_find_entry(dir, qstr, &page);
        if (de) {
                res = le64_to_cpu(de->inode);
                kunmap(page);
@@ -465,7 +465,7 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
 int nilfs_add_link(struct dentry *dentry, struct inode *inode)
 {
        struct inode *dir = dentry->d_parent->d_inode;
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        int namelen = dentry->d_name.len;
        unsigned chunk_size = nilfs_chunk_size(dir);
        unsigned reclen = NILFS_DIR_REC_LEN(namelen);
index 07ba838ef089231d5ba387d9220bb16402c193a2..ad6ed2cf19b4b5d31f3b7a80cfe11377b5acfceb 100644 (file)
@@ -67,7 +67,7 @@ nilfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        if (dentry->d_name.len > NILFS_NAME_LEN)
                return ERR_PTR(-ENAMETOOLONG);
 
-       ino = nilfs_inode_by_name(dir, dentry);
+       ino = nilfs_inode_by_name(dir, &dentry->d_name);
        inode = NULL;
        if (ino) {
                inode = nilfs_iget(dir->i_sb, ino);
@@ -81,10 +81,7 @@ struct dentry *nilfs_get_parent(struct dentry *child)
 {
        unsigned long ino;
        struct inode *inode;
-       struct dentry dotdot;
-
-       dotdot.d_name.name = "..";
-       dotdot.d_name.len = 2;
+       struct qstr dotdot = {.name = "..", .len = 2};
 
        ino = nilfs_inode_by_name(child->d_inode, &dotdot);
        if (!ino)
@@ -296,7 +293,7 @@ static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry)
        int err;
 
        err = -ENOENT;
-       de = nilfs_find_entry(dir, dentry, &page);
+       de = nilfs_find_entry(dir, &dentry->d_name, &page);
        if (!de)
                goto out;
 
@@ -389,7 +386,7 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                return err;
 
        err = -ENOENT;
-       old_de = nilfs_find_entry(old_dir, old_dentry, &old_page);
+       old_de = nilfs_find_entry(old_dir, &old_dentry->d_name, &old_page);
        if (!old_de)
                goto out;
 
@@ -409,7 +406,7 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        goto out_dir;
 
                err = -ENOENT;
-               new_de = nilfs_find_entry(new_dir, new_dentry, &new_page);
+               new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, &new_page);
                if (!new_de)
                        goto out_dir;
                inc_nlink(old_inode);
index 4da6f67e9a915aeb2096cc03cb47ec09a40f0338..8723e5bfd0717921ccbcfa526ed874d5eea2b8ea 100644 (file)
@@ -217,10 +217,10 @@ static inline int nilfs_init_acl(struct inode *inode, struct inode *dir)
 
 /* dir.c */
 extern int nilfs_add_link(struct dentry *, struct inode *);
-extern ino_t nilfs_inode_by_name(struct inode *, struct dentry *);
+extern ino_t nilfs_inode_by_name(struct inode *, const struct qstr *);
 extern int nilfs_make_empty(struct inode *, struct inode *);
 extern struct nilfs_dir_entry *
-nilfs_find_entry(struct inode *, struct dentry *, struct page **);
+nilfs_find_entry(struct inode *, const struct qstr *, struct page **);
 extern int nilfs_delete_entry(struct nilfs_dir_entry *, struct page *);
 extern int nilfs_empty_dir(struct inode *);
 extern struct nilfs_dir_entry *nilfs_dotdot(struct inode *, struct page **);
index a94e8bd8eb1f8274372940271970e0506ac3df50..472cdf29ef82598586a52f32c405bc43a8ab6b26 100644 (file)
 #include <linux/init.h> /* module_init */
 #include <linux/inotify.h>
 #include <linux/kernel.h> /* roundup() */
-#include <linux/magic.h> /* superblock magic number */
-#include <linux/mount.h> /* mntget */
 #include <linux/namei.h> /* LOOKUP_FOLLOW */
-#include <linux/path.h> /* struct path */
 #include <linux/sched.h> /* struct user */
 #include <linux/slab.h> /* struct kmem_cache */
 #include <linux/syscalls.h>
 #include <linux/types.h>
+#include <linux/anon_inodes.h>
 #include <linux/uaccess.h>
 #include <linux/poll.h>
 #include <linux/wait.h>
@@ -45,8 +43,6 @@
 
 #include <asm/ioctls.h>
 
-static struct vfsmount *inotify_mnt __read_mostly;
-
 /* these are configurable via /proc/sys/fs/inotify/ */
 static int inotify_max_user_instances __read_mostly;
 static int inotify_max_queued_events __read_mostly;
@@ -645,9 +641,7 @@ SYSCALL_DEFINE1(inotify_init1, int, flags)
 {
        struct fsnotify_group *group;
        struct user_struct *user;
-       struct file *filp;
-       struct path path;
-       int fd, ret;
+       int ret;
 
        /* Check the IN_* constants for consistency.  */
        BUILD_BUG_ON(IN_CLOEXEC != O_CLOEXEC);
@@ -656,10 +650,6 @@ SYSCALL_DEFINE1(inotify_init1, int, flags)
        if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
                return -EINVAL;
 
-       fd = get_unused_fd_flags(flags & O_CLOEXEC);
-       if (fd < 0)
-               return fd;
-
        user = get_current_user();
        if (unlikely(atomic_read(&user->inotify_devs) >=
                        inotify_max_user_instances)) {
@@ -676,27 +666,14 @@ SYSCALL_DEFINE1(inotify_init1, int, flags)
 
        atomic_inc(&user->inotify_devs);
 
-       path.mnt = inotify_mnt;
-       path.dentry = inotify_mnt->mnt_root;
-       path_get(&path);
-       filp = alloc_file(&path, FMODE_READ, &inotify_fops);
-       if (!filp)
-               goto Enfile;
+       ret = anon_inode_getfd("inotify", &inotify_fops, group,
+                                 O_RDONLY | flags);
+       if (ret >= 0)
+               return ret;
 
-       filp->f_flags = O_RDONLY | (flags & O_NONBLOCK);
-       filp->private_data = group;
-
-       fd_install(fd, filp);
-
-       return fd;
-
-Enfile:
-       ret = -ENFILE;
-       path_put(&path);
        atomic_dec(&user->inotify_devs);
 out_free_uid:
        free_uid(user);
-       put_unused_fd(fd);
        return ret;
 }
 
@@ -783,20 +760,6 @@ out:
        return ret;
 }
 
-static int
-inotify_get_sb(struct file_system_type *fs_type, int flags,
-              const char *dev_name, void *data, struct vfsmount *mnt)
-{
-       return get_sb_pseudo(fs_type, "inotify", NULL,
-                       INOTIFYFS_SUPER_MAGIC, mnt);
-}
-
-static struct file_system_type inotify_fs_type = {
-    .name      = "inotifyfs",
-    .get_sb    = inotify_get_sb,
-    .kill_sb   = kill_anon_super,
-};
-
 /*
  * inotify_user_setup - Our initialization function.  Note that we cannnot return
  * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
@@ -804,16 +767,6 @@ static struct file_system_type inotify_fs_type = {
  */
 static int __init inotify_user_setup(void)
 {
-       int ret;
-
-       ret = register_filesystem(&inotify_fs_type);
-       if (unlikely(ret))
-               panic("inotify: register_filesystem returned %d!\n", ret);
-
-       inotify_mnt = kern_mount(&inotify_fs_type);
-       if (IS_ERR(inotify_mnt))
-               panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
-
        inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark_entry, SLAB_PANIC);
        event_priv_cachep = KMEM_CACHE(inotify_event_private_data, SLAB_PANIC);
 
index 5a9e34475e37951eb03be7ae62d8c55666b53c48..9173e82a45d132f92caa4b066b762ee608e95e79 100644 (file)
@@ -1545,7 +1545,7 @@ static int ntfs_dir_fsync(struct file *filp, struct dentry *dentry,
                write_inode_now(bmp_vi, !datasync);
                iput(bmp_vi);
        }
-       ret = ntfs_write_inode(vi, 1);
+       ret = __ntfs_write_inode(vi, 1);
        write_inode_now(vi, !datasync);
        err = sync_blockdev(vi->i_sb->s_bdev);
        if (unlikely(err && !ret))
index 43179ddd336f542e54d198584b8a9e8295ccec9f..b681c71d70698f3b8df25e2d06cfe52e77aa4bc5 100644 (file)
@@ -2182,7 +2182,7 @@ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry,
        ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
        BUG_ON(S_ISDIR(vi->i_mode));
        if (!datasync || !NInoNonResident(NTFS_I(vi)))
-               ret = ntfs_write_inode(vi, 1);
+               ret = __ntfs_write_inode(vi, 1);
        write_inode_now(vi, !datasync);
        /*
         * NOTE: If we were to use mapping->private_list (see ext2 and
index dc2505abb6d7fd4b513cf63c3429863511b70e1d..4b57fb1eac2abe5a1e90d462c9febc8b1d6169d0 100644 (file)
@@ -2957,7 +2957,7 @@ out:
  *
  * Return 0 on success and -errno on error.
  */
-int ntfs_write_inode(struct inode *vi, int sync)
+int __ntfs_write_inode(struct inode *vi, int sync)
 {
        sle64 nt;
        ntfs_inode *ni = NTFS_I(vi);
index 117eaf8032a34ee44aacce0cc38d26c5f23772b3..9a113544605d5e69d3df07f1b55eea8b1a86cfde 100644 (file)
@@ -307,12 +307,12 @@ extern void ntfs_truncate_vfs(struct inode *vi);
 
 extern int ntfs_setattr(struct dentry *dentry, struct iattr *attr);
 
-extern int ntfs_write_inode(struct inode *vi, int sync);
+extern int __ntfs_write_inode(struct inode *vi, int sync);
 
 static inline void ntfs_commit_inode(struct inode *vi)
 {
        if (!is_bad_inode(vi))
-               ntfs_write_inode(vi, 1);
+               __ntfs_write_inode(vi, 1);
        return;
 }
 
index 80b04770e8e9c22a64a4becf0686260627e0d7e6..1cf39dfaee7a7395f89763528bf63eb957fec106 100644 (file)
@@ -39,6 +39,7 @@
 #include "dir.h"
 #include "debug.h"
 #include "index.h"
+#include "inode.h"
 #include "aops.h"
 #include "layout.h"
 #include "malloc.h"
@@ -2662,6 +2663,13 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs)
        return 0;
 }
 
+#ifdef NTFS_RW
+static int ntfs_write_inode(struct inode *vi, struct writeback_control *wbc)
+{
+       return __ntfs_write_inode(vi, wbc->sync_mode == WB_SYNC_ALL);
+}
+#endif
+
 /**
  * The complete super operations.
  */
index 2bbe1ecc08c060861acf366fd61dce1d2184e36d..9f8bd913c51ebfb1bd6463615d5dde04ee940708 100644 (file)
@@ -5713,7 +5713,7 @@ int ocfs2_remove_btree_range(struct inode *inode,
                goto out;
        }
 
-       vfs_dq_free_space_nodirty(inode,
+       dquot_free_space_nodirty(inode,
                                  ocfs2_clusters_to_bytes(inode->i_sb, len));
 
        ret = ocfs2_remove_extent(handle, et, cpos, len, meta_ac, dealloc);
@@ -6936,7 +6936,7 @@ static int ocfs2_do_truncate(struct ocfs2_super *osb,
                goto bail;
        }
 
-       vfs_dq_free_space_nodirty(inode,
+       dquot_free_space_nodirty(inode,
                        ocfs2_clusters_to_bytes(osb->sb, clusters_to_del));
        spin_lock(&OCFS2_I(inode)->ip_lock);
        OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters) -
@@ -7301,11 +7301,10 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
                unsigned int page_end;
                u64 phys;
 
-               if (vfs_dq_alloc_space_nodirty(inode,
-                                      ocfs2_clusters_to_bytes(osb->sb, 1))) {
-                       ret = -EDQUOT;
+               ret = dquot_alloc_space_nodirty(inode,
+                                      ocfs2_clusters_to_bytes(osb->sb, 1));
+               if (ret)
                        goto out_commit;
-               }
                did_quota = 1;
 
                ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off,
@@ -7381,7 +7380,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
 
 out_commit:
        if (ret < 0 && did_quota)
-               vfs_dq_free_space_nodirty(inode,
+               dquot_free_space_nodirty(inode,
                                          ocfs2_clusters_to_bytes(osb->sb, 1));
 
        ocfs2_commit_trans(osb, handle);
index 4c2a6d282c4d6a4c30c445ac0425838f3780079b..21441ddb550659d20b875da3d2d6a6c16cb33e5c 100644 (file)
@@ -1764,10 +1764,11 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
 
        wc->w_handle = handle;
 
-       if (clusters_to_alloc && vfs_dq_alloc_space_nodirty(inode,
-                       ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc))) {
-               ret = -EDQUOT;
-               goto out_commit;
+       if (clusters_to_alloc) {
+               ret = dquot_alloc_space_nodirty(inode,
+                       ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc));
+               if (ret)
+                       goto out_commit;
        }
        /*
         * We don't want this to fail in ocfs2_write_end(), so do it
@@ -1810,7 +1811,7 @@ success:
        return 0;
 out_quota:
        if (clusters_to_alloc)
-               vfs_dq_free_space(inode,
+               dquot_free_space(inode,
                          ocfs2_clusters_to_bytes(osb->sb, clusters_to_alloc));
 out_commit:
        ocfs2_commit_trans(osb, handle);
index 765d66c70989dc4499b59393048aa0ff1efefb91..efd77d071c80194cc2659ef99f92c2fbdce7e5ed 100644 (file)
@@ -2964,12 +2964,10 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
                goto out;
        }
 
-       if (vfs_dq_alloc_space_nodirty(dir,
-                               ocfs2_clusters_to_bytes(osb->sb,
-                                                       alloc + dx_alloc))) {
-               ret = -EDQUOT;
+       ret = dquot_alloc_space_nodirty(dir,
+               ocfs2_clusters_to_bytes(osb->sb, alloc + dx_alloc));
+       if (ret)
                goto out_commit;
-       }
        did_quota = 1;
 
        if (ocfs2_supports_indexed_dirs(osb) && !dx_inline) {
@@ -3178,7 +3176,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
 
 out_commit:
        if (ret < 0 && did_quota)
-               vfs_dq_free_space_nodirty(dir, bytes_allocated);
+               dquot_free_space_nodirty(dir, bytes_allocated);
 
        ocfs2_commit_trans(osb, handle);
 
@@ -3221,11 +3219,10 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
        if (extend) {
                u32 offset = OCFS2_I(dir)->ip_clusters;
 
-               if (vfs_dq_alloc_space_nodirty(dir,
-                                       ocfs2_clusters_to_bytes(sb, 1))) {
-                       status = -EDQUOT;
+               status = dquot_alloc_space_nodirty(dir,
+                                       ocfs2_clusters_to_bytes(sb, 1));
+               if (status)
                        goto bail;
-               }
                did_quota = 1;
 
                status = ocfs2_add_inode_data(OCFS2_SB(sb), dir, &offset,
@@ -3254,7 +3251,7 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
        status = 0;
 bail:
        if (did_quota && status < 0)
-               vfs_dq_free_space_nodirty(dir, ocfs2_clusters_to_bytes(sb, 1));
+               dquot_free_space_nodirty(dir, ocfs2_clusters_to_bytes(sb, 1));
        mlog_exit(status);
        return status;
 }
@@ -3889,11 +3886,10 @@ static int ocfs2_dx_dir_rebalance(struct ocfs2_super *osb, struct inode *dir,
                goto out;
        }
 
-       if (vfs_dq_alloc_space_nodirty(dir,
-                                      ocfs2_clusters_to_bytes(dir->i_sb, 1))) {
-               ret = -EDQUOT;
+       ret = dquot_alloc_space_nodirty(dir,
+                                      ocfs2_clusters_to_bytes(dir->i_sb, 1));
+       if (ret)
                goto out_commit;
-       }
        did_quota = 1;
 
        ret = ocfs2_journal_access_dl(handle, INODE_CACHE(dir), dx_leaf_bh,
@@ -3983,7 +3979,7 @@ static int ocfs2_dx_dir_rebalance(struct ocfs2_super *osb, struct inode *dir,
 
 out_commit:
        if (ret < 0 && did_quota)
-               vfs_dq_free_space_nodirty(dir,
+               dquot_free_space_nodirty(dir,
                                ocfs2_clusters_to_bytes(dir->i_sb, 1));
 
        ocfs2_commit_trans(osb, handle);
@@ -4165,11 +4161,10 @@ static int ocfs2_expand_inline_dx_root(struct inode *dir,
                goto out;
        }
 
-       if (vfs_dq_alloc_space_nodirty(dir,
-                                      ocfs2_clusters_to_bytes(osb->sb, 1))) {
-               ret = -EDQUOT;
+       ret = dquot_alloc_space_nodirty(dir,
+                                      ocfs2_clusters_to_bytes(osb->sb, 1));
+       if (ret)
                goto out_commit;
-       }
        did_quota = 1;
 
        /*
@@ -4229,7 +4224,7 @@ static int ocfs2_expand_inline_dx_root(struct inode *dir,
 
 out_commit:
        if (ret < 0 && did_quota)
-               vfs_dq_free_space_nodirty(dir,
+               dquot_free_space_nodirty(dir,
                                          ocfs2_clusters_to_bytes(dir->i_sb, 1));
 
        ocfs2_commit_trans(osb, handle);
index 5b52547d6299e75b673dda19d89e160c5cb429d2..17947dc8341e95d187ed13fa69f1a4bdaf5803dc 100644 (file)
@@ -107,6 +107,9 @@ static int ocfs2_file_open(struct inode *inode, struct file *file)
        mlog_entry("(0x%p, 0x%p, '%.*s')\n", inode, file,
                   file->f_path.dentry->d_name.len, file->f_path.dentry->d_name.name);
 
+       if (file->f_mode & FMODE_WRITE)
+               dquot_initialize(inode);
+
        spin_lock(&oi->ip_lock);
 
        /* Check that the inode hasn't been wiped from disk by another
@@ -629,11 +632,10 @@ restart_all:
        }
 
 restarted_transaction:
-       if (vfs_dq_alloc_space_nodirty(inode, ocfs2_clusters_to_bytes(osb->sb,
-           clusters_to_add))) {
-               status = -EDQUOT;
+       status = dquot_alloc_space_nodirty(inode,
+                       ocfs2_clusters_to_bytes(osb->sb, clusters_to_add));
+       if (status)
                goto leave;
-       }
        did_quota = 1;
 
        /* reserve a write to the file entry early on - that we if we
@@ -674,7 +676,7 @@ restarted_transaction:
        clusters_to_add -= (OCFS2_I(inode)->ip_clusters - prev_clusters);
        spin_unlock(&OCFS2_I(inode)->ip_lock);
        /* Release unused quota reservation */
-       vfs_dq_free_space(inode,
+       dquot_free_space(inode,
                        ocfs2_clusters_to_bytes(osb->sb, clusters_to_add));
        did_quota = 0;
 
@@ -710,7 +712,7 @@ restarted_transaction:
 
 leave:
        if (status < 0 && did_quota)
-               vfs_dq_free_space(inode,
+               dquot_free_space(inode,
                        ocfs2_clusters_to_bytes(osb->sb, clusters_to_add));
        if (handle) {
                ocfs2_commit_trans(osb, handle);
@@ -978,6 +980,8 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 
        size_change = S_ISREG(inode->i_mode) && attr->ia_valid & ATTR_SIZE;
        if (size_change) {
+               dquot_initialize(inode);
+
                status = ocfs2_rw_lock(inode, 1);
                if (status < 0) {
                        mlog_errno(status);
@@ -1020,7 +1024,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
                /*
                 * Gather pointers to quota structures so that allocation /
                 * freeing of quota structures happens here and not inside
-                * vfs_dq_transfer() where we have problems with lock ordering
+                * dquot_transfer() where we have problems with lock ordering
                 */
                if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
                    && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
@@ -1053,7 +1057,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
                        mlog_errno(status);
                        goto bail_unlock;
                }
-               status = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
+               status = dquot_transfer(inode, attr);
                if (status < 0)
                        goto bail_commit;
        } else {
index 88459bdd1ff37eb538485b796174086ffd421138..278a223aae14b8a2d347890735c729567e3840cc 100644 (file)
@@ -665,7 +665,7 @@ static int ocfs2_remove_inode(struct inode *inode,
        }
 
        ocfs2_remove_from_cache(INODE_CACHE(inode), di_bh);
-       vfs_dq_free_inode(inode);
+       dquot_free_inode(inode);
 
        status = ocfs2_free_dinode(handle, inode_alloc_inode,
                                   inode_alloc_bh, di);
@@ -971,6 +971,8 @@ void ocfs2_delete_inode(struct inode *inode)
                goto bail;
        }
 
+       dquot_initialize(inode);
+
        if (!ocfs2_inode_is_valid_to_delete(inode)) {
                /* It's probably not necessary to truncate_inode_pages
                 * here but we do it for safety anyway (it will most
@@ -1087,6 +1089,8 @@ void ocfs2_clear_inode(struct inode *inode)
        mlog_bug_on_msg(OCFS2_SB(inode->i_sb) == NULL,
                        "Inode=%lu\n", inode->i_ino);
 
+       dquot_drop(inode);
+
        /* To preven remote deletes we hold open lock before, now it
         * is time to unlock PR and EX open locks. */
        ocfs2_open_unlock(inode);
index 50fb26a6a5f55f88a93c84e29b9d4386e181cbdb..d9cd4e373a5309e24602b5dab9899f208a756523 100644 (file)
@@ -212,7 +212,7 @@ static struct inode *ocfs2_get_init_inode(struct inode *dir, int mode)
        } else
                inode->i_gid = current_fsgid();
        inode->i_mode = mode;
-       vfs_dq_init(inode);
+       dquot_initialize(inode);
        return inode;
 }
 
@@ -244,6 +244,8 @@ static int ocfs2_mknod(struct inode *dir,
                   (unsigned long)dev, dentry->d_name.len,
                   dentry->d_name.name);
 
+       dquot_initialize(dir);
+
        /* get our super block */
        osb = OCFS2_SB(dir->i_sb);
 
@@ -348,13 +350,9 @@ static int ocfs2_mknod(struct inode *dir,
                goto leave;
        }
 
-       /* We don't use standard VFS wrapper because we don't want vfs_dq_init
-        * to be called. */
-       if (sb_any_quota_active(osb->sb) &&
-           osb->sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
-               status = -EDQUOT;
+       status = dquot_alloc_inode(inode);
+       if (status)
                goto leave;
-       }
        did_quota_inode = 1;
 
        mlog_entry("(0x%p, 0x%p, %d, %lu, '%.*s')\n", dir, dentry,
@@ -431,7 +429,7 @@ static int ocfs2_mknod(struct inode *dir,
        status = 0;
 leave:
        if (status < 0 && did_quota_inode)
-               vfs_dq_free_inode(inode);
+               dquot_free_inode(inode);
        if (handle)
                ocfs2_commit_trans(osb, handle);
 
@@ -636,6 +634,8 @@ static int ocfs2_link(struct dentry *old_dentry,
        if (S_ISDIR(inode->i_mode))
                return -EPERM;
 
+       dquot_initialize(dir);
+
        err = ocfs2_inode_lock_nested(dir, &parent_fe_bh, 1, OI_LS_PARENT);
        if (err < 0) {
                if (err != -ENOENT)
@@ -791,6 +791,8 @@ static int ocfs2_unlink(struct inode *dir,
        mlog_entry("(0x%p, 0x%p, '%.*s')\n", dir, dentry,
                   dentry->d_name.len, dentry->d_name.name);
 
+       dquot_initialize(dir);
+
        BUG_ON(dentry->d_parent->d_inode != dir);
 
        mlog(0, "ino = %llu\n", (unsigned long long)OCFS2_I(inode)->ip_blkno);
@@ -1051,6 +1053,9 @@ static int ocfs2_rename(struct inode *old_dir,
                   old_dentry->d_name.len, old_dentry->d_name.name,
                   new_dentry->d_name.len, new_dentry->d_name.name);
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        osb = OCFS2_SB(old_dir->i_sb);
 
        if (new_inode) {
@@ -1599,6 +1604,8 @@ static int ocfs2_symlink(struct inode *dir,
        mlog_entry("(0x%p, 0x%p, symname='%s' actual='%.*s')\n", dir,
                   dentry, symname, dentry->d_name.len, dentry->d_name.name);
 
+       dquot_initialize(dir);
+
        sb = dir->i_sb;
        osb = OCFS2_SB(sb);
 
@@ -1688,13 +1695,9 @@ static int ocfs2_symlink(struct inode *dir,
                goto bail;
        }
 
-       /* We don't use standard VFS wrapper because we don't want vfs_dq_init
-        * to be called. */
-       if (sb_any_quota_active(osb->sb) &&
-           osb->sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
-               status = -EDQUOT;
+       status = dquot_alloc_inode(inode);
+       if (status)
                goto bail;
-       }
        did_quota_inode = 1;
 
        mlog_entry("(0x%p, 0x%p, %d, '%.*s')\n", dir, dentry,
@@ -1716,11 +1719,10 @@ static int ocfs2_symlink(struct inode *dir,
                u32 offset = 0;
 
                inode->i_op = &ocfs2_symlink_inode_operations;
-               if (vfs_dq_alloc_space_nodirty(inode,
-                   ocfs2_clusters_to_bytes(osb->sb, 1))) {
-                       status = -EDQUOT;
+               status = dquot_alloc_space_nodirty(inode,
+                   ocfs2_clusters_to_bytes(osb->sb, 1));
+               if (status)
                        goto bail;
-               }
                did_quota = 1;
                status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0,
                                              new_fe_bh,
@@ -1788,10 +1790,10 @@ static int ocfs2_symlink(struct inode *dir,
        d_instantiate(dentry, inode);
 bail:
        if (status < 0 && did_quota)
-               vfs_dq_free_space_nodirty(inode,
+               dquot_free_space_nodirty(inode,
                                        ocfs2_clusters_to_bytes(osb->sb, 1));
        if (status < 0 && did_quota_inode)
-               vfs_dq_free_inode(inode);
+               dquot_free_inode(inode);
        if (handle)
                ocfs2_commit_trans(osb, handle);
 
@@ -2099,13 +2101,9 @@ int ocfs2_create_inode_in_orphan(struct inode *dir,
                goto leave;
        }
 
-       /* We don't use standard VFS wrapper because we don't want vfs_dq_init
-        * to be called. */
-       if (sb_any_quota_active(osb->sb) &&
-           osb->sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA) {
-               status = -EDQUOT;
+       status = dquot_alloc_inode(inode);
+       if (status)
                goto leave;
-       }
        did_quota_inode = 1;
 
        inode->i_nlink = 0;
@@ -2140,7 +2138,7 @@ int ocfs2_create_inode_in_orphan(struct inode *dir,
        insert_inode_hash(inode);
 leave:
        if (status < 0 && did_quota_inode)
-               vfs_dq_free_inode(inode);
+               dquot_free_inode(inode);
        if (handle)
                ocfs2_commit_trans(osb, handle);
 
index b437dc0c4caddd814c824822ff93aa48d26afd65..355f41d1d520742b7ebb650333c705515c72f01e 100644 (file)
@@ -851,13 +851,6 @@ static void ocfs2_destroy_dquot(struct dquot *dquot)
 }
 
 const struct dquot_operations ocfs2_quota_operations = {
-       .initialize     = dquot_initialize,
-       .drop           = dquot_drop,
-       .alloc_space    = dquot_alloc_space,
-       .alloc_inode    = dquot_alloc_inode,
-       .free_space     = dquot_free_space,
-       .free_inode     = dquot_free_inode,
-       .transfer       = dquot_transfer,
        .write_dquot    = ocfs2_write_dquot,
        .acquire_dquot  = ocfs2_acquire_dquot,
        .release_dquot  = ocfs2_release_dquot,
index 21f9e71223caf53b10c3f2b128782aef14169889..a6467f3d262ee6bd720a732d396e23b3e4637ade 100644 (file)
@@ -457,7 +457,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
                        break;
                }
                dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
-               for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
+               for_each_set_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
                        qbh = NULL;
                        status = ocfs2_read_quota_block(lqinode,
                                                ol_dqblk_block(sb, chunk, bit),
index fb6aa7acf54b042053379ce1214a4a6386285666..9e96921dffda4f9f9b32970c142bacc59984802a 100644 (file)
@@ -4390,7 +4390,7 @@ static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir,
        }
 
        mutex_lock(&inode->i_mutex);
-       vfs_dq_init(dir);
+       dquot_initialize(dir);
        error = ocfs2_reflink(old_dentry, dir, new_dentry, preserve);
        mutex_unlock(&inode->i_mutex);
        if (!error)
index f3b7c1541f3a38f38c42be8eaf92f062170da55a..75d9b5ba1d451c9bbdb40fc0baeb49bcd825cf78 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/parser.h>
 #include <linux/buffer_head.h>
 #include <linux/vmalloc.h>
+#include <linux/writeback.h>
 #include <linux/crc-itu-t.h>
 #include "omfs.h"
 
@@ -89,7 +90,7 @@ static void omfs_update_checksums(struct omfs_inode *oi)
        oi->i_head.h_check_xor = xor;
 }
 
-static int omfs_write_inode(struct inode *inode, int wait)
+static int __omfs_write_inode(struct inode *inode, int wait)
 {
        struct omfs_inode *oi;
        struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
@@ -162,9 +163,14 @@ out:
        return ret;
 }
 
+static int omfs_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       return __omfs_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
 int omfs_sync_inode(struct inode *inode)
 {
-       return omfs_write_inode(inode, 1);
+       return __omfs_write_inode(inode, 1);
 }
 
 /*
index 040cef72bc00e46a0c58790f1c18cef7722756c8..e17f54454b5076b231e37c023abd31a1237445d5 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -8,7 +8,6 @@
 #include <linux/mm.h>
 #include <linux/file.h>
 #include <linux/fdtable.h>
-#include <linux/quotaops.h>
 #include <linux/fsnotify.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -271,17 +270,15 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)
         * Make sure that there are no leases.  get_write_access() protects
         * against the truncate racing with a lease-granting setlease().
         */
-       error = break_lease(inode, FMODE_WRITE);
+       error = break_lease(inode, O_WRONLY);
        if (error)
                goto put_write_and_out;
 
        error = locks_verify_truncate(inode, NULL, length);
        if (!error)
                error = security_path_truncate(&path, length, 0);
-       if (!error) {
-               vfs_dq_init(inode);
+       if (!error)
                error = do_truncate(path.dentry, length, 0, NULL);
-       }
 
 put_write_and_out:
        put_write_access(inode);
index 8d5f392ec3d39fa12a025ec3a6045b7ef8f790a4..5cc564a83149a5fc311b57d87793febbdcfd4f27 100644 (file)
@@ -86,7 +86,7 @@ static int do_make_slave(struct vfsmount *mnt)
 
        /*
         * slave 'mnt' to a peer mount that has the
-        * same root dentry. If none is available than
+        * same root dentry. If none is available then
         * slave it to anything that is available.
         */
        while ((peer_mnt = next_peer(peer_mnt)) != mnt &&
@@ -147,6 +147,11 @@ void change_mnt_propagation(struct vfsmount *mnt, int type)
  * get the next mount in the propagation tree.
  * @m: the mount seen last
  * @origin: the original mount from where the tree walk initiated
+ *
+ * Note that peer groups form contiguous segments of slave lists.
+ * We rely on that in get_source() to be able to find out if
+ * vfsmount found while iterating with propagation_next() is
+ * a peer of one we'd found earlier.
  */
 static struct vfsmount *propagation_next(struct vfsmount *m,
                                         struct vfsmount *origin)
@@ -186,10 +191,6 @@ static struct vfsmount *get_source(struct vfsmount *dest,
 {
        struct vfsmount *p_last_src = NULL;
        struct vfsmount *p_last_dest = NULL;
-       *type = CL_PROPAGATION;
-
-       if (IS_MNT_SHARED(dest))
-               *type |= CL_MAKE_SHARED;
 
        while (last_dest != dest->mnt_master) {
                p_last_dest = last_dest;
@@ -202,13 +203,18 @@ static struct vfsmount *get_source(struct vfsmount *dest,
                do {
                        p_last_dest = next_peer(p_last_dest);
                } while (IS_MNT_NEW(p_last_dest));
+               /* is that a peer of the earlier? */
+               if (dest == p_last_dest) {
+                       *type = CL_MAKE_SHARED;
+                       return p_last_src;
+               }
        }
-
-       if (dest != p_last_dest) {
-               *type |= CL_SLAVE;
-               return last_src;
-       } else
-               return p_last_src;
+       /* slave of the earlier, then */
+       *type = CL_SLAVE;
+       /* beginning of peer group among the slaves? */
+       if (IS_MNT_SHARED(dest))
+               *type |= CL_MAKE_SHARED;
+       return last_src;
 }
 
 /*
index 958665d662af24f4e7bea120a959c6220a91b1f5..1ea4ae1efcd3f898097a011360bb978c29f7b565 100644 (file)
 #define CL_SLAVE               0x02
 #define CL_COPY_ALL            0x04
 #define CL_MAKE_SHARED                 0x08
-#define CL_PROPAGATION                 0x10
-#define CL_PRIVATE             0x20
+#define CL_PRIVATE             0x10
 
 static inline void set_mnt_shared(struct vfsmount *mnt)
 {
-       mnt->mnt_flags &= ~MNT_PNODE_MASK;
+       mnt->mnt_flags &= ~MNT_SHARED_MASK;
        mnt->mnt_flags |= MNT_SHARED;
 }
 
index 18e20feee251b22fb7d89f5b220e46267621573d..aa8637b81028b573c4feb395541be560aab5a1a3 100644 (file)
@@ -273,7 +273,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
                rcu_read_lock();  /* FIXME: is this correct? */
                qsize = atomic_read(&__task_cred(p)->user->sigpending);
                rcu_read_unlock();
-               qlim = p->signal->rlim[RLIMIT_SIGPENDING].rlim_cur;
+               qlim = task_rlimit(p, RLIMIT_SIGPENDING);
                unlock_task_sighand(p, &flags);
        }
 
@@ -420,7 +420,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
                cutime = sig->cutime;
                cstime = sig->cstime;
                cgtime = sig->cgtime;
-               rsslim = sig->rlim[RLIMIT_RSS].rlim_cur;
+               rsslim = ACCESS_ONCE(sig->rlim[RLIMIT_RSS].rlim_cur);
 
                /* add up live thread stats at the group level */
                if (whole) {
index 623e2ffb5d2bb5ae7fbb153af881a24f6f9932a0..a7310841c83149e406c1089d30a8c27b2c1e67fb 100644 (file)
@@ -647,17 +647,11 @@ static int mounts_release(struct inode *inode, struct file *file)
 static unsigned mounts_poll(struct file *file, poll_table *wait)
 {
        struct proc_mounts *p = file->private_data;
-       struct mnt_namespace *ns = p->ns;
        unsigned res = POLLIN | POLLRDNORM;
 
-       poll_wait(file, &ns->poll, wait);
-
-       spin_lock(&vfsmount_lock);
-       if (p->event != ns->event) {
-               p->event = ns->event;
+       poll_wait(file, &p->ns->poll, wait);
+       if (mnt_had_events(p))
                res |= POLLERR | POLLPRI;
-       }
-       spin_unlock(&vfsmount_lock);
 
        return res;
 }
index 480cb1065eec8a65d9224075ffc66858dd4d738f..08f4d71dacd7fba3e9a704cb77c9131b6d0c5d69 100644 (file)
@@ -291,19 +291,17 @@ static const struct inode_operations proc_file_inode_operations = {
  * returns the struct proc_dir_entry for "/proc/tty/driver", and
  * returns "serial" in residual.
  */
-static int xlate_proc_name(const char *name,
-                          struct proc_dir_entry **ret, const char **residual)
+static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
+                            const char **residual)
 {
        const char              *cp = name, *next;
        struct proc_dir_entry   *de;
        int                     len;
-       int                     rtn = 0;
 
        de = *ret;
        if (!de)
                de = &proc_root;
 
-       spin_lock(&proc_subdir_lock);
        while (1) {
                next = strchr(cp, '/');
                if (!next)
@@ -315,16 +313,25 @@ static int xlate_proc_name(const char *name,
                                break;
                }
                if (!de) {
-                       rtn = -ENOENT;
-                       goto out;
+                       WARN(1, "name '%s'\n", name);
+                       return -ENOENT;
                }
                cp += len + 1;
        }
        *residual = cp;
        *ret = de;
-out:
+       return 0;
+}
+
+static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
+                          const char **residual)
+{
+       int rv;
+
+       spin_lock(&proc_subdir_lock);
+       rv = __xlate_proc_name(name, ret, residual);
        spin_unlock(&proc_subdir_lock);
-       return rtn;
+       return rv;
 }
 
 static DEFINE_IDA(proc_inum_ida);
@@ -662,6 +669,7 @@ struct proc_dir_entry *proc_symlink(const char *name,
        }
        return ent;
 }
+EXPORT_SYMBOL(proc_symlink);
 
 struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
                struct proc_dir_entry *parent)
@@ -700,6 +708,7 @@ struct proc_dir_entry *proc_mkdir(const char *name,
 {
        return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
 }
+EXPORT_SYMBOL(proc_mkdir);
 
 struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                                         struct proc_dir_entry *parent)
@@ -728,6 +737,7 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
        }
        return ent;
 }
+EXPORT_SYMBOL(create_proc_entry);
 
 struct proc_dir_entry *proc_create_data(const char *name, mode_t mode,
                                        struct proc_dir_entry *parent,
@@ -762,6 +772,7 @@ out_free:
 out:
        return NULL;
 }
+EXPORT_SYMBOL(proc_create_data);
 
 static void free_proc_entry(struct proc_dir_entry *de)
 {
@@ -793,11 +804,13 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
        const char *fn = name;
        int len;
 
-       if (xlate_proc_name(name, &parent, &fn) != 0)
+       spin_lock(&proc_subdir_lock);
+       if (__xlate_proc_name(name, &parent, &fn) != 0) {
+               spin_unlock(&proc_subdir_lock);
                return;
+       }
        len = strlen(fn);
 
-       spin_lock(&proc_subdir_lock);
        for (p = &parent->subdir; *p; p=&(*p)->next ) {
                if (proc_match(len, fn, *p)) {
                        de = *p;
@@ -807,8 +820,10 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
                }
        }
        spin_unlock(&proc_subdir_lock);
-       if (!de)
+       if (!de) {
+               WARN(1, "name '%s'\n", name);
                return;
+       }
 
        spin_lock(&de->pde_unload_lock);
        /*
@@ -853,3 +868,4 @@ continue_removing:
                        de->parent->name, de->name, de->subdir->name);
        pde_put(de);
 }
+EXPORT_SYMBOL(remove_proc_entry);
index b080b791d9e313b580882a0163de92fbb22886b9..757c069f2a65132584272d3fc1ccd22ea012d737 100644 (file)
@@ -220,9 +220,3 @@ void pid_ns_release_proc(struct pid_namespace *ns)
 {
        mntput(ns->proc_mnt);
 }
-
-EXPORT_SYMBOL(proc_symlink);
-EXPORT_SYMBOL(proc_mkdir);
-EXPORT_SYMBOL(create_proc_entry);
-EXPORT_SYMBOL(proc_create_data);
-EXPORT_SYMBOL(remove_proc_entry);
index f277c4a111cb7217d6002cceb8a488cce26c468f..183f8ff5f400b90d8b128465551d8157004f20d2 100644 (file)
@@ -16,7 +16,7 @@
 
 void task_mem(struct seq_file *m, struct mm_struct *mm)
 {
-       unsigned long data, text, lib;
+       unsigned long data, text, lib, swap;
        unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;
 
        /*
@@ -36,6 +36,7 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
        data = mm->total_vm - mm->shared_vm - mm->stack_vm;
        text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> 10;
        lib = (mm->exec_vm << (PAGE_SHIFT-10)) - text;
+       swap = get_mm_counter(mm, MM_SWAPENTS);
        seq_printf(m,
                "VmPeak:\t%8lu kB\n"
                "VmSize:\t%8lu kB\n"
@@ -46,7 +47,8 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
                "VmStk:\t%8lu kB\n"
                "VmExe:\t%8lu kB\n"
                "VmLib:\t%8lu kB\n"
-               "VmPTE:\t%8lu kB\n",
+               "VmPTE:\t%8lu kB\n"
+               "VmSwap:\t%8lu kB\n",
                hiwater_vm << (PAGE_SHIFT-10),
                (total_vm - mm->reserved_vm) << (PAGE_SHIFT-10),
                mm->locked_vm << (PAGE_SHIFT-10),
@@ -54,7 +56,8 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
                total_rss << (PAGE_SHIFT-10),
                data << (PAGE_SHIFT-10),
                mm->stack_vm << (PAGE_SHIFT-10), text, lib,
-               (PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10);
+               (PTRS_PER_PTE*sizeof(pte_t)*mm->nr_ptes) >> 10,
+               swap << (PAGE_SHIFT-10));
 }
 
 unsigned long task_vsize(struct mm_struct *mm)
@@ -65,11 +68,11 @@ unsigned long task_vsize(struct mm_struct *mm)
 int task_statm(struct mm_struct *mm, int *shared, int *text,
               int *data, int *resident)
 {
-       *shared = get_mm_counter(mm, file_rss);
+       *shared = get_mm_counter(mm, MM_FILEPAGES);
        *text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
                                                                >> PAGE_SHIFT;
        *data = mm->total_vm - mm->shared_vm;
-       *resident = *shared + get_mm_counter(mm, anon_rss);
+       *resident = *shared + get_mm_counter(mm, MM_ANONPAGES);
        return mm->total_vm;
 }
 
index efc02ebb8c70530147c6dbe3a72d1a0584aabd7c..dad7fb247ddc30a387a55870d051062c2d8c78f9 100644 (file)
@@ -59,3 +59,8 @@ config QUOTACTL
        bool
        depends on XFS_QUOTA || QUOTA
        default y
+
+config QUOTACTL_COMPAT
+       bool
+       depends on QUOTACTL && COMPAT_FOR_U64_ALIGNMENT
+       default y
index 68d4f6dc057833ee08c2b404594e60cfc514d888..5f9e9e276af09c837b4396b84e724f155908068d 100644 (file)
@@ -3,3 +3,5 @@ obj-$(CONFIG_QFMT_V1)           += quota_v1.o
 obj-$(CONFIG_QFMT_V2)          += quota_v2.o
 obj-$(CONFIG_QUOTA_TREE)       += quota_tree.o
 obj-$(CONFIG_QUOTACTL)         += quota.o
+obj-$(CONFIG_QUOTACTL_COMPAT)  += compat.o
+obj-$(CONFIG_QUOTA_NETLINK_INTERFACE)  += netlink.o
diff --git a/fs/quota/compat.c b/fs/quota/compat.c
new file mode 100644 (file)
index 0000000..fb1892f
--- /dev/null
@@ -0,0 +1,118 @@
+
+#include <linux/syscalls.h>
+#include <linux/compat.h>
+#include <linux/quotaops.h>
+
+/*
+ * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64)
+ * and is necessary due to alignment problems.
+ */
+struct compat_if_dqblk {
+       compat_u64 dqb_bhardlimit;
+       compat_u64 dqb_bsoftlimit;
+       compat_u64 dqb_curspace;
+       compat_u64 dqb_ihardlimit;
+       compat_u64 dqb_isoftlimit;
+       compat_u64 dqb_curinodes;
+       compat_u64 dqb_btime;
+       compat_u64 dqb_itime;
+       compat_uint_t dqb_valid;
+};
+
+/* XFS structures */
+struct compat_fs_qfilestat {
+       compat_u64 dqb_bhardlimit;
+       compat_u64 qfs_nblks;
+       compat_uint_t qfs_nextents;
+};
+
+struct compat_fs_quota_stat {
+       __s8            qs_version;
+       __u16           qs_flags;
+       __s8            qs_pad;
+       struct compat_fs_qfilestat      qs_uquota;
+       struct compat_fs_qfilestat      qs_gquota;
+       compat_uint_t   qs_incoredqs;
+       compat_int_t    qs_btimelimit;
+       compat_int_t    qs_itimelimit;
+       compat_int_t    qs_rtbtimelimit;
+       __u16           qs_bwarnlimit;
+       __u16           qs_iwarnlimit;
+};
+
+asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
+                                               qid_t id, void __user *addr)
+{
+       unsigned int cmds;
+       struct if_dqblk __user *dqblk;
+       struct compat_if_dqblk __user *compat_dqblk;
+       struct fs_quota_stat __user *fsqstat;
+       struct compat_fs_quota_stat __user *compat_fsqstat;
+       compat_uint_t data;
+       u16 xdata;
+       long ret;
+
+       cmds = cmd >> SUBCMDSHIFT;
+
+       switch (cmds) {
+       case Q_GETQUOTA:
+               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
+               compat_dqblk = addr;
+               ret = sys_quotactl(cmd, special, id, dqblk);
+               if (ret)
+                       break;
+               if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) ||
+                       get_user(data, &dqblk->dqb_valid) ||
+                       put_user(data, &compat_dqblk->dqb_valid))
+                       ret = -EFAULT;
+               break;
+       case Q_SETQUOTA:
+               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
+               compat_dqblk = addr;
+               ret = -EFAULT;
+               if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) ||
+                       get_user(data, &compat_dqblk->dqb_valid) ||
+                       put_user(data, &dqblk->dqb_valid))
+                       break;
+               ret = sys_quotactl(cmd, special, id, dqblk);
+               break;
+       case Q_XGETQSTAT:
+               fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
+               compat_fsqstat = addr;
+               ret = sys_quotactl(cmd, special, id, fsqstat);
+               if (ret)
+                       break;
+               ret = -EFAULT;
+               /* Copying qs_version, qs_flags, qs_pad */
+               if (copy_in_user(compat_fsqstat, fsqstat,
+                       offsetof(struct compat_fs_quota_stat, qs_uquota)))
+                       break;
+               /* Copying qs_uquota */
+               if (copy_in_user(&compat_fsqstat->qs_uquota,
+                       &fsqstat->qs_uquota,
+                       sizeof(compat_fsqstat->qs_uquota)) ||
+                       get_user(data, &fsqstat->qs_uquota.qfs_nextents) ||
+                       put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents))
+                       break;
+               /* Copying qs_gquota */
+               if (copy_in_user(&compat_fsqstat->qs_gquota,
+                       &fsqstat->qs_gquota,
+                       sizeof(compat_fsqstat->qs_gquota)) ||
+                       get_user(data, &fsqstat->qs_gquota.qfs_nextents) ||
+                       put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents))
+                       break;
+               /* Copying the rest */
+               if (copy_in_user(&compat_fsqstat->qs_incoredqs,
+                       &fsqstat->qs_incoredqs,
+                       sizeof(struct compat_fs_quota_stat) -
+                       offsetof(struct compat_fs_quota_stat, qs_incoredqs)) ||
+                       get_user(xdata, &fsqstat->qs_iwarnlimit) ||
+                       put_user(xdata, &compat_fsqstat->qs_iwarnlimit))
+                       break;
+               ret = 0;
+               break;
+       default:
+               ret = sys_quotactl(cmd, special, id, addr);
+       }
+       return ret;
+}
index 3fc62b097bedac541f2ada3309932b0953f8bbda..e0b870f4749f2ad01abab80d56e66759359f43c2 100644 (file)
  *
  * Any operation working on dquots via inode pointers must hold dqptr_sem.  If
  * operation is just reading pointers from inode (or not using them at all) the
- * read lock is enough. If pointers are altered function must hold write lock
- * (these locking rules also apply for S_NOQUOTA flag in the inode - note that
- * for altering the flag i_mutex is also needed).
+ * read lock is enough. If pointers are altered function must hold write lock.
+ * Special care needs to be taken about S_NOQUOTA inode flag (marking that
+ * inode is a quota file). Functions adding pointers from inode to dquots have
+ * to check this flag under dqptr_sem and then (if S_NOQUOTA is not set) they
+ * have to do all pointer modifications before dropping dqptr_sem. This makes
+ * sure they cannot race with quotaon which first sets S_NOQUOTA flag and
+ * then drops all pointers to dquots from an inode.
  *
  * Each dquot has its dq_lock mutex. Locked dquots might not be referenced
  * from inodes (dquot_alloc_space() and such don't check the dq_lock).
@@ -225,6 +229,9 @@ static struct hlist_head *dquot_hash;
 struct dqstats dqstats;
 EXPORT_SYMBOL(dqstats);
 
+static qsize_t inode_get_rsv_space(struct inode *inode);
+static void __dquot_initialize(struct inode *inode, int type);
+
 static inline unsigned int
 hashfn(const struct super_block *sb, unsigned int id, int type)
 {
@@ -564,7 +571,7 @@ out:
 }
 EXPORT_SYMBOL(dquot_scan_active);
 
-int vfs_quota_sync(struct super_block *sb, int type)
+int vfs_quota_sync(struct super_block *sb, int type, int wait)
 {
        struct list_head *dirty;
        struct dquot *dquot;
@@ -609,6 +616,33 @@ int vfs_quota_sync(struct super_block *sb, int type)
        spin_unlock(&dq_list_lock);
        mutex_unlock(&dqopt->dqonoff_mutex);
 
+       if (!wait || (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE))
+               return 0;
+
+       /* This is not very clever (and fast) but currently I don't know about
+        * any other simple way of getting quota data to disk and we must get
+        * them there for userspace to be visible... */
+       if (sb->s_op->sync_fs)
+               sb->s_op->sync_fs(sb, 1);
+       sync_blockdev(sb->s_bdev);
+
+       /*
+        * Now when everything is written we can discard the pagecache so
+        * that userspace sees the changes.
+        */
+       mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (type != -1 && cnt != type)
+                       continue;
+               if (!sb_has_quota_active(sb, cnt))
+                       continue;
+               mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex,
+                                 I_MUTEX_QUOTA);
+               truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
+               mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex);
+       }
+       mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
+
        return 0;
 }
 EXPORT_SYMBOL(vfs_quota_sync);
@@ -840,11 +874,14 @@ static int dqinit_needed(struct inode *inode, int type)
 static void add_dquot_ref(struct super_block *sb, int type)
 {
        struct inode *inode, *old_inode = NULL;
+       int reserved = 0;
 
        spin_lock(&inode_lock);
        list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
                if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW))
                        continue;
+               if (unlikely(inode_get_rsv_space(inode) > 0))
+                       reserved = 1;
                if (!atomic_read(&inode->i_writecount))
                        continue;
                if (!dqinit_needed(inode, type))
@@ -854,7 +891,7 @@ static void add_dquot_ref(struct super_block *sb, int type)
                spin_unlock(&inode_lock);
 
                iput(old_inode);
-               sb->dq_op->initialize(inode, type);
+               __dquot_initialize(inode, type);
                /* We hold a reference to 'inode' so it couldn't have been
                 * removed from s_inodes list while we dropped the inode_lock.
                 * We cannot iput the inode now as we can be holding the last
@@ -865,6 +902,12 @@ static void add_dquot_ref(struct super_block *sb, int type)
        }
        spin_unlock(&inode_lock);
        iput(old_inode);
+
+       if (reserved) {
+               printk(KERN_WARNING "VFS (%s): Writes happened before quota"
+                       " was turned on thus quota information is probably "
+                       "inconsistent. Please run quotacheck(8).\n", sb->s_id);
+       }
 }
 
 /*
@@ -978,10 +1021,12 @@ static inline void dquot_resv_space(struct dquot *dquot, qsize_t number)
 /*
  * Claim reserved quota space
  */
-static void dquot_claim_reserved_space(struct dquot *dquot,
-                                               qsize_t number)
+static void dquot_claim_reserved_space(struct dquot *dquot, qsize_t number)
 {
-       WARN_ON(dquot->dq_dqb.dqb_rsvspace < number);
+       if (dquot->dq_dqb.dqb_rsvspace < number) {
+               WARN_ON_ONCE(1);
+               number = dquot->dq_dqb.dqb_rsvspace;
+       }
        dquot->dq_dqb.dqb_curspace += number;
        dquot->dq_dqb.dqb_rsvspace -= number;
 }
@@ -989,7 +1034,12 @@ static void dquot_claim_reserved_space(struct dquot *dquot,
 static inline
 void dquot_free_reserved_space(struct dquot *dquot, qsize_t number)
 {
-       dquot->dq_dqb.dqb_rsvspace -= number;
+       if (dquot->dq_dqb.dqb_rsvspace >= number)
+               dquot->dq_dqb.dqb_rsvspace -= number;
+       else {
+               WARN_ON_ONCE(1);
+               dquot->dq_dqb.dqb_rsvspace = 0;
+       }
 }
 
 static void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
@@ -1131,13 +1181,13 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
        *warntype = QUOTA_NL_NOWARN;
        if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
            test_bit(DQ_FAKE_B, &dquot->dq_flags))
-               return QUOTA_OK;
+               return 0;
 
        if (dquot->dq_dqb.dqb_ihardlimit &&
            newinodes > dquot->dq_dqb.dqb_ihardlimit &&
             !ignore_hardlimit(dquot)) {
                *warntype = QUOTA_NL_IHARDWARN;
-               return NO_QUOTA;
+               return -EDQUOT;
        }
 
        if (dquot->dq_dqb.dqb_isoftlimit &&
@@ -1146,7 +1196,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
            get_seconds() >= dquot->dq_dqb.dqb_itime &&
             !ignore_hardlimit(dquot)) {
                *warntype = QUOTA_NL_ISOFTLONGWARN;
-               return NO_QUOTA;
+               return -EDQUOT;
        }
 
        if (dquot->dq_dqb.dqb_isoftlimit &&
@@ -1157,7 +1207,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
                    sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
        }
 
-       return QUOTA_OK;
+       return 0;
 }
 
 /* needs dq_data_lock */
@@ -1169,7 +1219,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
        *warntype = QUOTA_NL_NOWARN;
        if (!sb_has_quota_limits_enabled(sb, dquot->dq_type) ||
            test_bit(DQ_FAKE_B, &dquot->dq_flags))
-               return QUOTA_OK;
+               return 0;
 
        tspace = dquot->dq_dqb.dqb_curspace + dquot->dq_dqb.dqb_rsvspace
                + space;
@@ -1179,7 +1229,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
             !ignore_hardlimit(dquot)) {
                if (!prealloc)
                        *warntype = QUOTA_NL_BHARDWARN;
-               return NO_QUOTA;
+               return -EDQUOT;
        }
 
        if (dquot->dq_dqb.dqb_bsoftlimit &&
@@ -1189,7 +1239,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
             !ignore_hardlimit(dquot)) {
                if (!prealloc)
                        *warntype = QUOTA_NL_BSOFTLONGWARN;
-               return NO_QUOTA;
+               return -EDQUOT;
        }
 
        if (dquot->dq_dqb.dqb_bsoftlimit &&
@@ -1205,10 +1255,10 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
                         * We don't allow preallocation to exceed softlimit so exceeding will
                         * be always printed
                         */
-                       return NO_QUOTA;
+                       return -EDQUOT;
        }
 
-       return QUOTA_OK;
+       return 0;
 }
 
 static int info_idq_free(struct dquot *dquot, qsize_t inodes)
@@ -1242,25 +1292,32 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space)
                return QUOTA_NL_BHARDBELOW;
        return QUOTA_NL_NOWARN;
 }
+
 /*
- *     Initialize quota pointers in inode
- *     We do things in a bit complicated way but by that we avoid calling
- *     dqget() and thus filesystem callbacks under dqptr_sem.
+ * Initialize quota pointers in inode
+ *
+ * We do things in a bit complicated way but by that we avoid calling
+ * dqget() and thus filesystem callbacks under dqptr_sem.
+ *
+ * It is better to call this function outside of any transaction as it
+ * might need a lot of space in journal for dquot structure allocation.
  */
-int dquot_initialize(struct inode *inode, int type)
+static void __dquot_initialize(struct inode *inode, int type)
 {
        unsigned int id = 0;
-       int cnt, ret = 0;
-       struct dquot *got[MAXQUOTAS] = { NULL, NULL };
+       int cnt;
+       struct dquot *got[MAXQUOTAS];
        struct super_block *sb = inode->i_sb;
+       qsize_t rsv;
 
        /* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
-       if (IS_NOQUOTA(inode))
-               return 0;
+       if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode))
+               return;
 
        /* First get references to structures we might need. */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               got[cnt] = NULL;
                if (type != -1 && cnt != type)
                        continue;
                switch (cnt) {
@@ -1275,7 +1332,6 @@ int dquot_initialize(struct inode *inode, int type)
        }
 
        down_write(&sb_dqopt(sb)->dqptr_sem);
-       /* Having dqptr_sem we know NOQUOTA flags can't be altered... */
        if (IS_NOQUOTA(inode))
                goto out_err;
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@@ -1287,20 +1343,31 @@ int dquot_initialize(struct inode *inode, int type)
                if (!inode->i_dquot[cnt]) {
                        inode->i_dquot[cnt] = got[cnt];
                        got[cnt] = NULL;
+                       /*
+                        * Make quota reservation system happy if someone
+                        * did a write before quota was turned on
+                        */
+                       rsv = inode_get_rsv_space(inode);
+                       if (unlikely(rsv))
+                               dquot_resv_space(inode->i_dquot[cnt], rsv);
                }
        }
 out_err:
        up_write(&sb_dqopt(sb)->dqptr_sem);
        /* Drop unused references */
        dqput_all(got);
-       return ret;
+}
+
+void dquot_initialize(struct inode *inode)
+{
+       __dquot_initialize(inode, -1);
 }
 EXPORT_SYMBOL(dquot_initialize);
 
 /*
  *     Release all quotas referenced by inode
  */
-int dquot_drop(struct inode *inode)
+static void __dquot_drop(struct inode *inode)
 {
        int cnt;
        struct dquot *put[MAXQUOTAS];
@@ -1312,32 +1379,31 @@ int dquot_drop(struct inode *inode)
        }
        up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
        dqput_all(put);
-       return 0;
 }
-EXPORT_SYMBOL(dquot_drop);
 
-/* Wrapper to remove references to quota structures from inode */
-void vfs_dq_drop(struct inode *inode)
-{
-       /* Here we can get arbitrary inode from clear_inode() so we have
-        * to be careful. OTOH we don't need locking as quota operations
-        * are allowed to change only at mount time */
-       if (!IS_NOQUOTA(inode) && inode->i_sb && inode->i_sb->dq_op
-           && inode->i_sb->dq_op->drop) {
-               int cnt;
-               /* Test before calling to rule out calls from proc and such
-                 * where we are not allowed to block. Note that this is
-                * actually reliable test even without the lock - the caller
-                * must assure that nobody can come after the DQUOT_DROP and
-                * add quota pointers back anyway */
-               for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-                       if (inode->i_dquot[cnt])
-                               break;
-               if (cnt < MAXQUOTAS)
-                       inode->i_sb->dq_op->drop(inode);
-       }
-}
-EXPORT_SYMBOL(vfs_dq_drop);
+void dquot_drop(struct inode *inode)
+{
+       int cnt;
+
+       if (IS_NOQUOTA(inode))
+               return;
+
+       /*
+        * Test before calling to rule out calls from proc and such
+        * where we are not allowed to block. Note that this is
+        * actually reliable test even without the lock - the caller
+        * must assure that nobody can come after the DQUOT_DROP and
+        * add quota pointers back anyway.
+        */
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt])
+                       break;
+       }
+
+       if (cnt < MAXQUOTAS)
+               __dquot_drop(inode);
+}
+EXPORT_SYMBOL(dquot_drop);
 
 /*
  * inode_reserved_space is managed internally by quota, and protected by
@@ -1351,28 +1417,30 @@ static qsize_t *inode_reserved_space(struct inode * inode)
        return inode->i_sb->dq_op->get_reserved_space(inode);
 }
 
-static void inode_add_rsv_space(struct inode *inode, qsize_t number)
+void inode_add_rsv_space(struct inode *inode, qsize_t number)
 {
        spin_lock(&inode->i_lock);
        *inode_reserved_space(inode) += number;
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL(inode_add_rsv_space);
 
-
-static void inode_claim_rsv_space(struct inode *inode, qsize_t number)
+void inode_claim_rsv_space(struct inode *inode, qsize_t number)
 {
        spin_lock(&inode->i_lock);
        *inode_reserved_space(inode) -= number;
        __inode_add_bytes(inode, number);
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL(inode_claim_rsv_space);
 
-static void inode_sub_rsv_space(struct inode *inode, qsize_t number)
+void inode_sub_rsv_space(struct inode *inode, qsize_t number)
 {
        spin_lock(&inode->i_lock);
        *inode_reserved_space(inode) -= number;
        spin_unlock(&inode->i_lock);
 }
+EXPORT_SYMBOL(inode_sub_rsv_space);
 
 static qsize_t inode_get_rsv_space(struct inode *inode)
 {
@@ -1404,38 +1472,34 @@ static void inode_decr_space(struct inode *inode, qsize_t number, int reserve)
 }
 
 /*
- * Following four functions update i_blocks+i_bytes fields and
- * quota information (together with appropriate checks)
- * NOTE: We absolutely rely on the fact that caller dirties
- * the inode (usually macros in quotaops.h care about this) and
- * holds a handle for the current transaction so that dquot write and
- * inode write go into the same transaction.
+ * This functions updates i_blocks+i_bytes fields and quota information
+ * (together with appropriate checks).
+ *
+ * NOTE: We absolutely rely on the fact that caller dirties the inode
+ * (usually helpers in quotaops.h care about this) and holds a handle for
+ * the current transaction so that dquot write and inode write go into the
+ * same transaction.
  */
 
 /*
  * This operation can block, but only after everything is updated
  */
 int __dquot_alloc_space(struct inode *inode, qsize_t number,
-                       int warn, int reserve)
+               int warn, int reserve)
 {
-       int cnt, ret = QUOTA_OK;
+       int cnt, ret = 0;
        char warntype[MAXQUOTAS];
 
        /*
         * First test before acquiring mutex - solves deadlocks when we
         * re-enter the quota code and are already holding the mutex
         */
-       if (IS_NOQUOTA(inode)) {
+       if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) {
                inode_incr_space(inode, number, reserve);
                goto out;
        }
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       if (IS_NOQUOTA(inode)) {
-               inode_incr_space(inode, number, reserve);
-               goto out_unlock;
-       }
-
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
                warntype[cnt] = QUOTA_NL_NOWARN;
 
@@ -1443,9 +1507,9 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (!inode->i_dquot[cnt])
                        continue;
-               if (check_bdq(inode->i_dquot[cnt], number, warn, warntype+cnt)
-                   == NO_QUOTA) {
-                       ret = NO_QUOTA;
+               ret = check_bdq(inode->i_dquot[cnt], number, !warn,
+                               warntype+cnt);
+               if (ret) {
                        spin_unlock(&dq_data_lock);
                        goto out_flush_warn;
                }
@@ -1466,61 +1530,45 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
        mark_all_dquot_dirty(inode->i_dquot);
 out_flush_warn:
        flush_warnings(inode->i_dquot, warntype);
-out_unlock:
        up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
 out:
        return ret;
 }
-
-int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
-{
-       return __dquot_alloc_space(inode, number, warn, 0);
-}
-EXPORT_SYMBOL(dquot_alloc_space);
-
-int dquot_reserve_space(struct inode *inode, qsize_t number, int warn)
-{
-       return __dquot_alloc_space(inode, number, warn, 1);
-}
-EXPORT_SYMBOL(dquot_reserve_space);
+EXPORT_SYMBOL(__dquot_alloc_space);
 
 /*
  * This operation can block, but only after everything is updated
  */
-int dquot_alloc_inode(const struct inode *inode, qsize_t number)
+int dquot_alloc_inode(const struct inode *inode)
 {
-       int cnt, ret = NO_QUOTA;
+       int cnt, ret = 0;
        char warntype[MAXQUOTAS];
 
        /* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
-       if (IS_NOQUOTA(inode))
-               return QUOTA_OK;
+       if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode))
+               return 0;
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
                warntype[cnt] = QUOTA_NL_NOWARN;
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       if (IS_NOQUOTA(inode)) {
-               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-               return QUOTA_OK;
-       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (!inode->i_dquot[cnt])
                        continue;
-               if (check_idq(inode->i_dquot[cnt], number, warntype+cnt)
-                   == NO_QUOTA)
+               ret = check_idq(inode->i_dquot[cnt], 1, warntype + cnt);
+               if (ret)
                        goto warn_put_all;
        }
 
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (!inode->i_dquot[cnt])
                        continue;
-               dquot_incr_inodes(inode->i_dquot[cnt], number);
+               dquot_incr_inodes(inode->i_dquot[cnt], 1);
        }
-       ret = QUOTA_OK;
+
 warn_put_all:
        spin_unlock(&dq_data_lock);
-       if (ret == QUOTA_OK)
+       if (ret == 0)
                mark_all_dquot_dirty(inode->i_dquot);
        flush_warnings(inode->i_dquot, warntype);
        up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
@@ -1528,23 +1576,19 @@ warn_put_all:
 }
 EXPORT_SYMBOL(dquot_alloc_inode);
 
-int dquot_claim_space(struct inode *inode, qsize_t number)
+/*
+ * Convert in-memory reserved quotas to real consumed quotas
+ */
+int dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
 {
        int cnt;
-       int ret = QUOTA_OK;
 
-       if (IS_NOQUOTA(inode)) {
+       if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) {
                inode_claim_rsv_space(inode, number);
-               goto out;
+               return 0;
        }
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       if (IS_NOQUOTA(inode))  {
-               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-               inode_claim_rsv_space(inode, number);
-               goto out;
-       }
-
        spin_lock(&dq_data_lock);
        /* Claim reserved quotas to allocated quotas */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@@ -1557,33 +1601,26 @@ int dquot_claim_space(struct inode *inode, qsize_t number)
        spin_unlock(&dq_data_lock);
        mark_all_dquot_dirty(inode->i_dquot);
        up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-out:
-       return ret;
+       return 0;
 }
-EXPORT_SYMBOL(dquot_claim_space);
+EXPORT_SYMBOL(dquot_claim_space_nodirty);
 
 /*
  * This operation can block, but only after everything is updated
  */
-int __dquot_free_space(struct inode *inode, qsize_t number, int reserve)
+void __dquot_free_space(struct inode *inode, qsize_t number, int reserve)
 {
        unsigned int cnt;
        char warntype[MAXQUOTAS];
 
        /* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
-       if (IS_NOQUOTA(inode)) {
-out_sub:
+       if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) {
                inode_decr_space(inode, number, reserve);
-               return QUOTA_OK;
+               return;
        }
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       /* Now recheck reliably when holding dqptr_sem */
-       if (IS_NOQUOTA(inode)) {
-               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-               goto out_sub;
-       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (!inode->i_dquot[cnt])
@@ -1603,56 +1640,34 @@ out_sub:
 out_unlock:
        flush_warnings(inode->i_dquot, warntype);
        up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       return QUOTA_OK;
-}
-
-int dquot_free_space(struct inode *inode, qsize_t number)
-{
-       return  __dquot_free_space(inode, number, 0);
 }
-EXPORT_SYMBOL(dquot_free_space);
-
-/*
- * Release reserved quota space
- */
-void dquot_release_reserved_space(struct inode *inode, qsize_t number)
-{
-       __dquot_free_space(inode, number, 1);
-
-}
-EXPORT_SYMBOL(dquot_release_reserved_space);
+EXPORT_SYMBOL(__dquot_free_space);
 
 /*
  * This operation can block, but only after everything is updated
  */
-int dquot_free_inode(const struct inode *inode, qsize_t number)
+void dquot_free_inode(const struct inode *inode)
 {
        unsigned int cnt;
        char warntype[MAXQUOTAS];
 
        /* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
-       if (IS_NOQUOTA(inode))
-               return QUOTA_OK;
+       if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode))
+               return;
 
        down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       /* Now recheck reliably when holding dqptr_sem */
-       if (IS_NOQUOTA(inode)) {
-               up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-               return QUOTA_OK;
-       }
        spin_lock(&dq_data_lock);
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (!inode->i_dquot[cnt])
                        continue;
-               warntype[cnt] = info_idq_free(inode->i_dquot[cnt], number);
-               dquot_decr_inodes(inode->i_dquot[cnt], number);
+               warntype[cnt] = info_idq_free(inode->i_dquot[cnt], 1);
+               dquot_decr_inodes(inode->i_dquot[cnt], 1);
        }
        spin_unlock(&dq_data_lock);
        mark_all_dquot_dirty(inode->i_dquot);
        flush_warnings(inode->i_dquot, warntype);
        up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       return QUOTA_OK;
 }
 EXPORT_SYMBOL(dquot_free_inode);
 
@@ -1662,37 +1677,31 @@ EXPORT_SYMBOL(dquot_free_inode);
  * This operation can block, but only after everything is updated
  * A transaction must be started when entering this function.
  */
-int dquot_transfer(struct inode *inode, struct iattr *iattr)
+static int __dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask)
 {
        qsize_t space, cur_space;
        qsize_t rsv_space = 0;
        struct dquot *transfer_from[MAXQUOTAS];
        struct dquot *transfer_to[MAXQUOTAS];
-       int cnt, ret = QUOTA_OK;
-       int chuid = iattr->ia_valid & ATTR_UID && inode->i_uid != iattr->ia_uid,
-           chgid = iattr->ia_valid & ATTR_GID && inode->i_gid != iattr->ia_gid;
+       int cnt, ret = 0;
        char warntype_to[MAXQUOTAS];
        char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];
 
        /* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
        if (IS_NOQUOTA(inode))
-               return QUOTA_OK;
+               return 0;
        /* Initialize the arrays */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                transfer_from[cnt] = NULL;
                transfer_to[cnt] = NULL;
                warntype_to[cnt] = QUOTA_NL_NOWARN;
        }
-       if (chuid)
-               transfer_to[USRQUOTA] = dqget(inode->i_sb, iattr->ia_uid,
-                                             USRQUOTA);
-       if (chgid)
-               transfer_to[GRPQUOTA] = dqget(inode->i_sb, iattr->ia_gid,
-                                             GRPQUOTA);
-
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (mask & (1 << cnt))
+                       transfer_to[cnt] = dqget(inode->i_sb, chid[cnt], cnt);
+       }
        down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
-       /* Now recheck reliably when holding dqptr_sem */
        if (IS_NOQUOTA(inode)) {        /* File without quota accounting? */
                up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
                goto put_all;
@@ -1706,9 +1715,11 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
                if (!transfer_to[cnt])
                        continue;
                transfer_from[cnt] = inode->i_dquot[cnt];
-               if (check_idq(transfer_to[cnt], 1, warntype_to + cnt) ==
-                   NO_QUOTA || check_bdq(transfer_to[cnt], space, 0,
-                   warntype_to + cnt) == NO_QUOTA)
+               ret = check_idq(transfer_to[cnt], 1, warntype_to + cnt);
+               if (ret)
+                       goto over_quota;
+               ret = check_bdq(transfer_to[cnt], space, 0, warntype_to + cnt);
+               if (ret)
                        goto over_quota;
        }
 
@@ -1762,22 +1773,32 @@ over_quota:
        /* Clear dquot pointers we don't want to dqput() */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
                transfer_from[cnt] = NULL;
-       ret = NO_QUOTA;
        goto warn_put_all;
 }
-EXPORT_SYMBOL(dquot_transfer);
 
-/* Wrapper for transferring ownership of an inode */
-int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
+/* Wrapper for transferring ownership of an inode for uid/gid only
+ * Called from FSXXX_setattr()
+ */
+int dquot_transfer(struct inode *inode, struct iattr *iattr)
 {
+       qid_t chid[MAXQUOTAS];
+       unsigned long mask = 0;
+
+       if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) {
+               mask |= 1 << USRQUOTA;
+               chid[USRQUOTA] = iattr->ia_uid;
+       }
+       if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid) {
+               mask |= 1 << GRPQUOTA;
+               chid[GRPQUOTA] = iattr->ia_gid;
+       }
        if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) {
-               vfs_dq_init(inode);
-               if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA)
-                       return 1;
+               dquot_initialize(inode);
+               return __dquot_transfer(inode, chid, mask);
        }
        return 0;
 }
-EXPORT_SYMBOL(vfs_dq_transfer);
+EXPORT_SYMBOL(dquot_transfer);
 
 /*
  * Write info of quota file to disk
@@ -1798,13 +1819,6 @@ EXPORT_SYMBOL(dquot_commit_info);
  * Definitions of diskquota operations.
  */
 const struct dquot_operations dquot_operations = {
-       .initialize     = dquot_initialize,
-       .drop           = dquot_drop,
-       .alloc_space    = dquot_alloc_space,
-       .alloc_inode    = dquot_alloc_inode,
-       .free_space     = dquot_free_space,
-       .free_inode     = dquot_free_inode,
-       .transfer       = dquot_transfer,
        .write_dquot    = dquot_commit,
        .acquire_dquot  = dquot_acquire,
        .release_dquot  = dquot_release,
@@ -1814,6 +1828,20 @@ const struct dquot_operations dquot_operations = {
        .destroy_dquot  = dquot_destroy,
 };
 
+/*
+ * Generic helper for ->open on filesystems supporting disk quotas.
+ */
+int dquot_file_open(struct inode *inode, struct file *file)
+{
+       int error;
+
+       error = generic_file_open(inode, file);
+       if (!error && (file->f_mode & FMODE_WRITE))
+               dquot_initialize(inode);
+       return error;
+}
+EXPORT_SYMBOL(dquot_file_open);
+
 /*
  * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
  */
@@ -1993,11 +2021,13 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
        }
 
        if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
-               /* As we bypass the pagecache we must now flush the inode so
-                * that we see all the changes from userspace... */
-               write_inode_now(inode, 1);
-               /* And now flush the block cache so that kernel sees the
-                * changes */
+               /* As we bypass the pagecache we must now flush all the
+                * dirty data and invalidate caches so that kernel sees
+                * changes from userspace. It is not enough to just flush
+                * the quota file since if blocksize < pagesize, invalidation
+                * of the cache could fail because of other unrelated dirty
+                * data */
+               sync_filesystem(sb);
                invalidate_bdev(sb->s_bdev);
        }
        mutex_lock(&dqopt->dqonoff_mutex);
@@ -2010,14 +2040,16 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
                /* We don't want quota and atime on quota files (deadlocks
                 * possible) Also nobody should write to the file - we use
                 * special IO operations which ignore the immutable bit. */
-               down_write(&dqopt->dqptr_sem);
                mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
                oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE |
                                             S_NOQUOTA);
                inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
                mutex_unlock(&inode->i_mutex);
-               up_write(&dqopt->dqptr_sem);
-               sb->dq_op->drop(inode);
+               /*
+                * When S_NOQUOTA is set, remove dquot references as no more
+                * references can be added
+                */
+               __dquot_drop(inode);
        }
 
        error = -EIO;
@@ -2053,14 +2085,12 @@ out_file_init:
        iput(inode);
 out_lock:
        if (oldflags != -1) {
-               down_write(&dqopt->dqptr_sem);
                mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
                /* Set the flags back (in the case of accidental quotaon()
                 * on a wrong file we don't want to mess up the flags) */
                inode->i_flags &= ~(S_NOATIME | S_NOQUOTA | S_IMMUTABLE);
                inode->i_flags |= oldflags;
                mutex_unlock(&inode->i_mutex);
-               up_write(&dqopt->dqptr_sem);
        }
        mutex_unlock(&dqopt->dqonoff_mutex);
 out_fmt:
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
new file mode 100644 (file)
index 0000000..2663ed9
--- /dev/null
@@ -0,0 +1,95 @@
+
+#include <linux/cred.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/quotaops.h>
+#include <linux/sched.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+/* Netlink family structure for quota */
+static struct genl_family quota_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = 0,
+       .name = "VFS_DQUOT",
+       .version = 1,
+       .maxattr = QUOTA_NL_A_MAX,
+};
+
+/**
+ * quota_send_warning - Send warning to userspace about exceeded quota
+ * @type: The quota type: USRQQUOTA, GRPQUOTA,...
+ * @id: The user or group id of the quota that was exceeded
+ * @dev: The device on which the fs is mounted (sb->s_dev)
+ * @warntype: The type of the warning: QUOTA_NL_...
+ *
+ * This can be used by filesystems (including those which don't use
+ * dquot) to send a message to userspace relating to quota limits.
+ *
+ */
+
+void quota_send_warning(short type, unsigned int id, dev_t dev,
+                       const char warntype)
+{
+       static atomic_t seq;
+       struct sk_buff *skb;
+       void *msg_head;
+       int ret;
+       int msg_size = 4 * nla_total_size(sizeof(u32)) +
+                      2 * nla_total_size(sizeof(u64));
+
+       /* We have to allocate using GFP_NOFS as we are called from a
+        * filesystem performing write and thus further recursion into
+        * the fs to free some data could cause deadlocks. */
+       skb = genlmsg_new(msg_size, GFP_NOFS);
+       if (!skb) {
+               printk(KERN_ERR
+                 "VFS: Not enough memory to send quota warning.\n");
+               return;
+       }
+       msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
+                       &quota_genl_family, 0, QUOTA_NL_C_WARNING);
+       if (!msg_head) {
+               printk(KERN_ERR
+                 "VFS: Cannot store netlink header in quota warning.\n");
+               goto err_out;
+       }
+       ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id);
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR, MAJOR(dev));
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
+       if (ret)
+               goto attr_err_out;
+       ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
+       if (ret)
+               goto attr_err_out;
+       genlmsg_end(skb, msg_head);
+
+       genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS);
+       return;
+attr_err_out:
+       printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
+err_out:
+       kfree_skb(skb);
+}
+EXPORT_SYMBOL(quota_send_warning);
+
+static int __init quota_init(void)
+{
+       if (genl_register_family(&quota_genl_family) != 0)
+               printk(KERN_ERR
+                      "VFS: Failed to create quota netlink interface.\n");
+       return 0;
+};
+
+module_init(quota_init);
index ee91e2756950eedc62e3f44934a23601cc8de6ea..95388f9b7356df637959fadf96f6b5ae2ea9eaa6 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/slab.h>
 #include <asm/current.h>
 #include <asm/uaccess.h>
-#include <linux/compat.h>
 #include <linux/kernel.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/capability.h>
 #include <linux/quotaops.h>
 #include <linux/types.h>
-#include <net/netlink.h>
-#include <net/genetlink.h>
+#include <linux/writeback.h>
 
-/* Check validity of generic quotactl commands */
-static int generic_quotactl_valid(struct super_block *sb, int type, int cmd,
-                                 qid_t id)
+static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
+                                    qid_t id)
 {
-       if (type >= MAXQUOTAS)
-               return -EINVAL;
-       if (!sb && cmd != Q_SYNC)
-               return -ENODEV;
-       /* Is operation supported? */
-       if (sb && !sb->s_qcop)
-               return -ENOSYS;
-
        switch (cmd) {
-               case Q_GETFMT:
-                       break;
-               case Q_QUOTAON:
-                       if (!sb->s_qcop->quota_on)
-                               return -ENOSYS;
-                       break;
-               case Q_QUOTAOFF:
-                       if (!sb->s_qcop->quota_off)
-                               return -ENOSYS;
-                       break;
-               case Q_SETINFO:
-                       if (!sb->s_qcop->set_info)
-                               return -ENOSYS;
-                       break;
-               case Q_GETINFO:
-                       if (!sb->s_qcop->get_info)
-                               return -ENOSYS;
-                       break;
-               case Q_SETQUOTA:
-                       if (!sb->s_qcop->set_dqblk)
-                               return -ENOSYS;
-                       break;
-               case Q_GETQUOTA:
-                       if (!sb->s_qcop->get_dqblk)
-                               return -ENOSYS;
-                       break;
-               case Q_SYNC:
-                       if (sb && !sb->s_qcop->quota_sync)
-                               return -ENOSYS;
+       /* these commands do not require any special privilegues */
+       case Q_GETFMT:
+       case Q_SYNC:
+       case Q_GETINFO:
+       case Q_XGETQSTAT:
+       case Q_XQUOTASYNC:
+               break;
+       /* allow to query information for dquots we "own" */
+       case Q_GETQUOTA:
+       case Q_XGETQUOTA:
+               if ((type == USRQUOTA && current_euid() == id) ||
+                   (type == GRPQUOTA && in_egroup_p(id)))
                        break;
-               default:
-                       return -EINVAL;
+               /*FALLTHROUGH*/
+       default:
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
        }
 
-       /* Is quota turned on for commands which need it? */
-       switch (cmd) {
-               case Q_GETFMT:
-               case Q_GETINFO:
-               case Q_SETINFO:
-               case Q_SETQUOTA:
-               case Q_GETQUOTA:
-                       /* This is just an informative test so we are satisfied
-                        * without the lock */
-                       if (!sb_has_quota_active(sb, type))
-                               return -ESRCH;
-       }
+       return security_quotactl(cmd, type, id, sb);
+}
 
-       /* Check privileges */
-       if (cmd == Q_GETQUOTA) {
-               if (((type == USRQUOTA && current_euid() != id) ||
-                    (type == GRPQUOTA && !in_egroup_p(id))) &&
-                   !capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+static int quota_sync_all(int type)
+{
+       struct super_block *sb;
+       int ret;
+
+       if (type >= MAXQUOTAS)
+               return -EINVAL;
+       ret = security_quotactl(Q_SYNC, type, 0, NULL);
+       if (ret)
+               return ret;
+
+       spin_lock(&sb_lock);
+restart:
+       list_for_each_entry(sb, &super_blocks, s_list) {
+               if (!sb->s_qcop || !sb->s_qcop->quota_sync)
+                       continue;
+
+               sb->s_count++;
+               spin_unlock(&sb_lock);
+               down_read(&sb->s_umount);
+               if (sb->s_root)
+                       sb->s_qcop->quota_sync(sb, type, 1);
+               up_read(&sb->s_umount);
+               spin_lock(&sb_lock);
+               if (__put_super_and_need_restart(sb))
+                       goto restart;
        }
-       else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO)
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       spin_unlock(&sb_lock);
 
        return 0;
 }
 
-/* Check validity of XFS Quota Manager commands */
-static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd,
-                             qid_t id)
+static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
+                        void __user *addr)
 {
-       if (type >= XQM_MAXQUOTAS)
-               return -EINVAL;
-       if (!sb)
-               return -ENODEV;
-       if (!sb->s_qcop)
-               return -ENOSYS;
+       char *pathname;
+       int ret = -ENOSYS;
+
+       pathname = getname(addr);
+       if (IS_ERR(pathname))
+               return PTR_ERR(pathname);
+       if (sb->s_qcop->quota_on)
+               ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0);
+       putname(pathname);
+       return ret;
+}
 
-       switch (cmd) {
-               case Q_XQUOTAON:
-               case Q_XQUOTAOFF:
-               case Q_XQUOTARM:
-                       if (!sb->s_qcop->set_xstate)
-                               return -ENOSYS;
-                       break;
-               case Q_XGETQSTAT:
-                       if (!sb->s_qcop->get_xstate)
-                               return -ENOSYS;
-                       break;
-               case Q_XSETQLIM:
-                       if (!sb->s_qcop->set_xquota)
-                               return -ENOSYS;
-                       break;
-               case Q_XGETQUOTA:
-                       if (!sb->s_qcop->get_xquota)
-                               return -ENOSYS;
-                       break;
-               case Q_XQUOTASYNC:
-                       if (!sb->s_qcop->quota_sync)
-                               return -ENOSYS;
-                       break;
-               default:
-                       return -EINVAL;
-       }
+static int quota_getfmt(struct super_block *sb, int type, void __user *addr)
+{
+       __u32 fmt;
 
-       /* Check privileges */
-       if (cmd == Q_XGETQUOTA) {
-               if (((type == XQM_USRQUOTA && current_euid() != id) ||
-                    (type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
-                    !capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-       } else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) {
-               if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+       down_read(&sb_dqopt(sb)->dqptr_sem);
+       if (!sb_has_quota_active(sb, type)) {
+               up_read(&sb_dqopt(sb)->dqptr_sem);
+               return -ESRCH;
        }
+       fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
+       up_read(&sb_dqopt(sb)->dqptr_sem);
+       if (copy_to_user(addr, &fmt, sizeof(fmt)))
+               return -EFAULT;
+       return 0;
+}
 
+static int quota_getinfo(struct super_block *sb, int type, void __user *addr)
+{
+       struct if_dqinfo info;
+       int ret;
+
+       if (!sb_has_quota_active(sb, type))
+               return -ESRCH;
+       if (!sb->s_qcop->get_info)
+               return -ENOSYS;
+       ret = sb->s_qcop->get_info(sb, type, &info);
+       if (!ret && copy_to_user(addr, &info, sizeof(info)))
+               return -EFAULT;
+       return ret;
+}
+
+static int quota_setinfo(struct super_block *sb, int type, void __user *addr)
+{
+       struct if_dqinfo info;
+
+       if (copy_from_user(&info, addr, sizeof(info)))
+               return -EFAULT;
+       if (!sb_has_quota_active(sb, type))
+               return -ESRCH;
+       if (!sb->s_qcop->set_info)
+               return -ENOSYS;
+       return sb->s_qcop->set_info(sb, type, &info);
+}
+
+static int quota_getquota(struct super_block *sb, int type, qid_t id,
+                         void __user *addr)
+{
+       struct if_dqblk idq;
+       int ret;
+
+       if (!sb_has_quota_active(sb, type))
+               return -ESRCH;
+       if (!sb->s_qcop->get_dqblk)
+               return -ENOSYS;
+       ret = sb->s_qcop->get_dqblk(sb, type, id, &idq);
+       if (ret)
+               return ret;
+       if (copy_to_user(addr, &idq, sizeof(idq)))
+               return -EFAULT;
        return 0;
 }
 
-static int check_quotactl_valid(struct super_block *sb, int type, int cmd,
-                               qid_t id)
+static int quota_setquota(struct super_block *sb, int type, qid_t id,
+                         void __user *addr)
 {
-       int error;
-
-       if (XQM_COMMAND(cmd))
-               error = xqm_quotactl_valid(sb, type, cmd, id);
-       else
-               error = generic_quotactl_valid(sb, type, cmd, id);
-       if (!error)
-               error = security_quotactl(cmd, type, id, sb);
-       return error;
+       struct if_dqblk idq;
+
+       if (copy_from_user(&idq, addr, sizeof(idq)))
+               return -EFAULT;
+       if (!sb_has_quota_active(sb, type))
+               return -ESRCH;
+       if (!sb->s_qcop->set_dqblk)
+               return -ENOSYS;
+       return sb->s_qcop->set_dqblk(sb, type, id, &idq);
 }
 
-#ifdef CONFIG_QUOTA
-void sync_quota_sb(struct super_block *sb, int type)
+static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr)
 {
-       int cnt;
+       __u32 flags;
 
-       if (!sb->s_qcop->quota_sync)
-               return;
+       if (copy_from_user(&flags, addr, sizeof(flags)))
+               return -EFAULT;
+       if (!sb->s_qcop->set_xstate)
+               return -ENOSYS;
+       return sb->s_qcop->set_xstate(sb, flags, cmd);
+}
 
-       sb->s_qcop->quota_sync(sb, type);
+static int quota_getxstate(struct super_block *sb, void __user *addr)
+{
+       struct fs_quota_stat fqs;
+       int ret;
 
-       if (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE)
-               return;
-       /* This is not very clever (and fast) but currently I don't know about
-        * any other simple way of getting quota data to disk and we must get
-        * them there for userspace to be visible... */
-       if (sb->s_op->sync_fs)
-               sb->s_op->sync_fs(sb, 1);
-       sync_blockdev(sb->s_bdev);
+       if (!sb->s_qcop->get_xstate)
+               return -ENOSYS;
+       ret = sb->s_qcop->get_xstate(sb, &fqs);
+       if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
+               return -EFAULT;
+       return ret;
+}
 
-       /*
-        * Now when everything is written we can discard the pagecache so
-        * that userspace sees the changes.
-        */
-       mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
-       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               if (type != -1 && cnt != type)
-                       continue;
-               if (!sb_has_quota_active(sb, cnt))
-                       continue;
-               mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex,
-                                 I_MUTEX_QUOTA);
-               truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
-               mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex);
-       }
-       mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
+static int quota_setxquota(struct super_block *sb, int type, qid_t id,
+                          void __user *addr)
+{
+       struct fs_disk_quota fdq;
+
+       if (copy_from_user(&fdq, addr, sizeof(fdq)))
+               return -EFAULT;
+       if (!sb->s_qcop->set_xquota)
+               return -ENOSYS;
+       return sb->s_qcop->set_xquota(sb, type, id, &fdq);
 }
-#endif
 
-static void sync_dquots(int type)
+static int quota_getxquota(struct super_block *sb, int type, qid_t id,
+                          void __user *addr)
 {
-       struct super_block *sb;
-       int cnt;
+       struct fs_disk_quota fdq;
+       int ret;
 
-       spin_lock(&sb_lock);
-restart:
-       list_for_each_entry(sb, &super_blocks, s_list) {
-               /* This test just improves performance so it needn't be
-                * reliable... */
-               for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-                       if (type != -1 && type != cnt)
-                               continue;
-                       if (!sb_has_quota_active(sb, cnt))
-                               continue;
-                       if (!info_dirty(&sb_dqopt(sb)->info[cnt]) &&
-                          list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list))
-                               continue;
-                       break;
-               }
-               if (cnt == MAXQUOTAS)
-                       continue;
-               sb->s_count++;
-               spin_unlock(&sb_lock);
-               down_read(&sb->s_umount);
-               if (sb->s_root)
-                       sync_quota_sb(sb, type);
-               up_read(&sb->s_umount);
-               spin_lock(&sb_lock);
-               if (__put_super_and_need_restart(sb))
-                       goto restart;
-       }
-       spin_unlock(&sb_lock);
+       if (!sb->s_qcop->get_xquota)
+               return -ENOSYS;
+       ret = sb->s_qcop->get_xquota(sb, type, id, &fdq);
+       if (!ret && copy_to_user(addr, &fdq, sizeof(fdq)))
+               return -EFAULT;
+       return ret;
 }
 
 /* Copy parameters and call proper function */
@@ -240,117 +224,55 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
 {
        int ret;
 
+       if (type >= (XQM_COMMAND(cmd) ? XQM_MAXQUOTAS : MAXQUOTAS))
+               return -EINVAL;
+       if (!sb->s_qcop)
+               return -ENOSYS;
+
+       ret = check_quotactl_permission(sb, type, cmd, id);
+       if (ret < 0)
+               return ret;
+
        switch (cmd) {
-               case Q_QUOTAON: {
-                       char *pathname;
-
-                       pathname = getname(addr);
-                       if (IS_ERR(pathname))
-                               return PTR_ERR(pathname);
-                       ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0);
-                       putname(pathname);
-                       return ret;
-               }
-               case Q_QUOTAOFF:
-                       return sb->s_qcop->quota_off(sb, type, 0);
-
-               case Q_GETFMT: {
-                       __u32 fmt;
-
-                       down_read(&sb_dqopt(sb)->dqptr_sem);
-                       if (!sb_has_quota_active(sb, type)) {
-                               up_read(&sb_dqopt(sb)->dqptr_sem);
-                               return -ESRCH;
-                       }
-                       fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
-                       up_read(&sb_dqopt(sb)->dqptr_sem);
-                       if (copy_to_user(addr, &fmt, sizeof(fmt)))
-                               return -EFAULT;
-                       return 0;
-               }
-               case Q_GETINFO: {
-                       struct if_dqinfo info;
-
-                       ret = sb->s_qcop->get_info(sb, type, &info);
-                       if (ret)
-                               return ret;
-                       if (copy_to_user(addr, &info, sizeof(info)))
-                               return -EFAULT;
-                       return 0;
-               }
-               case Q_SETINFO: {
-                       struct if_dqinfo info;
-
-                       if (copy_from_user(&info, addr, sizeof(info)))
-                               return -EFAULT;
-                       return sb->s_qcop->set_info(sb, type, &info);
-               }
-               case Q_GETQUOTA: {
-                       struct if_dqblk idq;
-
-                       ret = sb->s_qcop->get_dqblk(sb, type, id, &idq);
-                       if (ret)
-                               return ret;
-                       if (copy_to_user(addr, &idq, sizeof(idq)))
-                               return -EFAULT;
-                       return 0;
-               }
-               case Q_SETQUOTA: {
-                       struct if_dqblk idq;
-
-                       if (copy_from_user(&idq, addr, sizeof(idq)))
-                               return -EFAULT;
-                       return sb->s_qcop->set_dqblk(sb, type, id, &idq);
-               }
-               case Q_SYNC:
-                       if (sb)
-                               sync_quota_sb(sb, type);
-                       else
-                               sync_dquots(type);
-                       return 0;
-
-               case Q_XQUOTAON:
-               case Q_XQUOTAOFF:
-               case Q_XQUOTARM: {
-                       __u32 flags;
-
-                       if (copy_from_user(&flags, addr, sizeof(flags)))
-                               return -EFAULT;
-                       return sb->s_qcop->set_xstate(sb, flags, cmd);
-               }
-               case Q_XGETQSTAT: {
-                       struct fs_quota_stat fqs;
-               
-                       if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
-                               return ret;
-                       if (copy_to_user(addr, &fqs, sizeof(fqs)))
-                               return -EFAULT;
-                       return 0;
-               }
-               case Q_XSETQLIM: {
-                       struct fs_disk_quota fdq;
-
-                       if (copy_from_user(&fdq, addr, sizeof(fdq)))
-                               return -EFAULT;
-                      return sb->s_qcop->set_xquota(sb, type, id, &fdq);
-               }
-               case Q_XGETQUOTA: {
-                       struct fs_disk_quota fdq;
-
-                       ret = sb->s_qcop->get_xquota(sb, type, id, &fdq);
-                       if (ret)
-                               return ret;
-                       if (copy_to_user(addr, &fdq, sizeof(fdq)))
-                               return -EFAULT;
-                       return 0;
-               }
-               case Q_XQUOTASYNC:
-                       return sb->s_qcop->quota_sync(sb, type);
-               /* We never reach here unless validity check is broken */
-               default:
-                       BUG();
+       case Q_QUOTAON:
+               return quota_quotaon(sb, type, cmd, id, addr);
+       case Q_QUOTAOFF:
+               if (!sb->s_qcop->quota_off)
+                       return -ENOSYS;
+               return sb->s_qcop->quota_off(sb, type, 0);
+       case Q_GETFMT:
+               return quota_getfmt(sb, type, addr);
+       case Q_GETINFO:
+               return quota_getinfo(sb, type, addr);
+       case Q_SETINFO:
+               return quota_setinfo(sb, type, addr);
+       case Q_GETQUOTA:
+               return quota_getquota(sb, type, id, addr);
+       case Q_SETQUOTA:
+               return quota_setquota(sb, type, id, addr);
+       case Q_SYNC:
+               if (!sb->s_qcop->quota_sync)
+                       return -ENOSYS;
+               return sb->s_qcop->quota_sync(sb, type, 1);
+       case Q_XQUOTAON:
+       case Q_XQUOTAOFF:
+       case Q_XQUOTARM:
+               return quota_setxstate(sb, cmd, addr);
+       case Q_XGETQSTAT:
+               return quota_getxstate(sb, addr);
+       case Q_XSETQLIM:
+               return quota_setxquota(sb, type, id, addr);
+       case Q_XGETQUOTA:
+               return quota_getxquota(sb, type, id, addr);
+       case Q_XQUOTASYNC:
+               /* caller already holds s_umount */
+               if (sb->s_flags & MS_RDONLY)
+                       return -EROFS;
+               writeback_inodes_sb(sb);
+               return 0;
+       default:
+               return -EINVAL;
        }
-       return 0;
 }
 
 /*
@@ -397,224 +319,23 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
        cmds = cmd >> SUBCMDSHIFT;
        type = cmd & SUBCMDMASK;
 
-       if (cmds != Q_SYNC || special) {
-               sb = quotactl_block(special);
-               if (IS_ERR(sb))
-                       return PTR_ERR(sb);
+       /*
+        * As a special case Q_SYNC can be called without a specific device.
+        * It will iterate all superblocks that have quota enabled and call
+        * the sync action on each of them.
+        */
+       if (!special) {
+               if (cmds == Q_SYNC)
+                       return quota_sync_all(type);
+               return -ENODEV;
        }
 
-       ret = check_quotactl_valid(sb, type, cmds, id);
-       if (ret >= 0)
-               ret = do_quotactl(sb, type, cmds, id, addr);
-       if (sb)
-               drop_super(sb);
+       sb = quotactl_block(special);
+       if (IS_ERR(sb))
+               return PTR_ERR(sb);
 
-       return ret;
-}
-
-#if defined(CONFIG_COMPAT_FOR_U64_ALIGNMENT)
-/*
- * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64)
- * and is necessary due to alignment problems.
- */
-struct compat_if_dqblk {
-       compat_u64 dqb_bhardlimit;
-       compat_u64 dqb_bsoftlimit;
-       compat_u64 dqb_curspace;
-       compat_u64 dqb_ihardlimit;
-       compat_u64 dqb_isoftlimit;
-       compat_u64 dqb_curinodes;
-       compat_u64 dqb_btime;
-       compat_u64 dqb_itime;
-       compat_uint_t dqb_valid;
-};
-
-/* XFS structures */
-struct compat_fs_qfilestat {
-       compat_u64 dqb_bhardlimit;
-       compat_u64 qfs_nblks;
-       compat_uint_t qfs_nextents;
-};
-
-struct compat_fs_quota_stat {
-       __s8            qs_version;
-       __u16           qs_flags;
-       __s8            qs_pad;
-       struct compat_fs_qfilestat      qs_uquota;
-       struct compat_fs_qfilestat      qs_gquota;
-       compat_uint_t   qs_incoredqs;
-       compat_int_t    qs_btimelimit;
-       compat_int_t    qs_itimelimit;
-       compat_int_t    qs_rtbtimelimit;
-       __u16           qs_bwarnlimit;
-       __u16           qs_iwarnlimit;
-};
-
-asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
-                                               qid_t id, void __user *addr)
-{
-       unsigned int cmds;
-       struct if_dqblk __user *dqblk;
-       struct compat_if_dqblk __user *compat_dqblk;
-       struct fs_quota_stat __user *fsqstat;
-       struct compat_fs_quota_stat __user *compat_fsqstat;
-       compat_uint_t data;
-       u16 xdata;
-       long ret;
+       ret = do_quotactl(sb, type, cmds, id, addr);
 
-       cmds = cmd >> SUBCMDSHIFT;
-
-       switch (cmds) {
-       case Q_GETQUOTA:
-               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
-               compat_dqblk = addr;
-               ret = sys_quotactl(cmd, special, id, dqblk);
-               if (ret)
-                       break;
-               if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) ||
-                       get_user(data, &dqblk->dqb_valid) ||
-                       put_user(data, &compat_dqblk->dqb_valid))
-                       ret = -EFAULT;
-               break;
-       case Q_SETQUOTA:
-               dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
-               compat_dqblk = addr;
-               ret = -EFAULT;
-               if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) ||
-                       get_user(data, &compat_dqblk->dqb_valid) ||
-                       put_user(data, &dqblk->dqb_valid))
-                       break;
-               ret = sys_quotactl(cmd, special, id, dqblk);
-               break;
-       case Q_XGETQSTAT:
-               fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
-               compat_fsqstat = addr;
-               ret = sys_quotactl(cmd, special, id, fsqstat);
-               if (ret)
-                       break;
-               ret = -EFAULT;
-               /* Copying qs_version, qs_flags, qs_pad */
-               if (copy_in_user(compat_fsqstat, fsqstat,
-                       offsetof(struct compat_fs_quota_stat, qs_uquota)))
-                       break;
-               /* Copying qs_uquota */
-               if (copy_in_user(&compat_fsqstat->qs_uquota,
-                       &fsqstat->qs_uquota,
-                       sizeof(compat_fsqstat->qs_uquota)) ||
-                       get_user(data, &fsqstat->qs_uquota.qfs_nextents) ||
-                       put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents))
-                       break;
-               /* Copying qs_gquota */
-               if (copy_in_user(&compat_fsqstat->qs_gquota,
-                       &fsqstat->qs_gquota,
-                       sizeof(compat_fsqstat->qs_gquota)) ||
-                       get_user(data, &fsqstat->qs_gquota.qfs_nextents) ||
-                       put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents))
-                       break;
-               /* Copying the rest */
-               if (copy_in_user(&compat_fsqstat->qs_incoredqs,
-                       &fsqstat->qs_incoredqs,
-                       sizeof(struct compat_fs_quota_stat) -
-                       offsetof(struct compat_fs_quota_stat, qs_incoredqs)) ||
-                       get_user(xdata, &fsqstat->qs_iwarnlimit) ||
-                       put_user(xdata, &compat_fsqstat->qs_iwarnlimit))
-                       break;
-               ret = 0;
-               break;
-       default:
-               ret = sys_quotactl(cmd, special, id, addr);
-       }
+       drop_super(sb);
        return ret;
 }
-#endif
-
-
-#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
-
-/* Netlink family structure for quota */
-static struct genl_family quota_genl_family = {
-       .id = GENL_ID_GENERATE,
-       .hdrsize = 0,
-       .name = "VFS_DQUOT",
-       .version = 1,
-       .maxattr = QUOTA_NL_A_MAX,
-};
-
-/**
- * quota_send_warning - Send warning to userspace about exceeded quota
- * @type: The quota type: USRQQUOTA, GRPQUOTA,...
- * @id: The user or group id of the quota that was exceeded
- * @dev: The device on which the fs is mounted (sb->s_dev)
- * @warntype: The type of the warning: QUOTA_NL_...
- *
- * This can be used by filesystems (including those which don't use
- * dquot) to send a message to userspace relating to quota limits.
- *
- */
-
-void quota_send_warning(short type, unsigned int id, dev_t dev,
-                       const char warntype)
-{
-       static atomic_t seq;
-       struct sk_buff *skb;
-       void *msg_head;
-       int ret;
-       int msg_size = 4 * nla_total_size(sizeof(u32)) +
-                      2 * nla_total_size(sizeof(u64));
-
-       /* We have to allocate using GFP_NOFS as we are called from a
-        * filesystem performing write and thus further recursion into
-        * the fs to free some data could cause deadlocks. */
-       skb = genlmsg_new(msg_size, GFP_NOFS);
-       if (!skb) {
-               printk(KERN_ERR
-                 "VFS: Not enough memory to send quota warning.\n");
-               return;
-       }
-       msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
-                       &quota_genl_family, 0, QUOTA_NL_C_WARNING);
-       if (!msg_head) {
-               printk(KERN_ERR
-                 "VFS: Cannot store netlink header in quota warning.\n");
-               goto err_out;
-       }
-       ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
-       if (ret)
-               goto attr_err_out;
-       ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id);
-       if (ret)
-               goto attr_err_out;
-       ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
-       if (ret)
-               goto attr_err_out;
-       ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR, MAJOR(dev));
-       if (ret)
-               goto attr_err_out;
-       ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
-       if (ret)
-               goto attr_err_out;
-       ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
-       if (ret)
-               goto attr_err_out;
-       genlmsg_end(skb, msg_head);
-
-       genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS);
-       return;
-attr_err_out:
-       printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
-err_out:
-       kfree_skb(skb);
-}
-EXPORT_SYMBOL(quota_send_warning);
-
-static int __init quota_init(void)
-{
-       if (genl_register_family(&quota_genl_family) != 0)
-               printk(KERN_ERR
-                      "VFS: Failed to create quota netlink interface.\n");
-       return 0;
-};
-
-module_init(quota_init);
-#endif
-
index 65c872761177e10d5b074d97800e5245208c9fe1..dc014f7def0523869e970574c722473d74f1e316 100644 (file)
@@ -425,7 +425,7 @@ static void _reiserfs_free_block(struct reiserfs_transaction_handle *th,
 
        journal_mark_dirty(th, s, sbh);
        if (for_unformatted)
-               vfs_dq_free_block_nodirty(inode, 1);
+               dquot_free_block_nodirty(inode, 1);
 }
 
 void reiserfs_free_block(struct reiserfs_transaction_handle *th,
@@ -1049,7 +1049,7 @@ static inline int blocknrs_and_prealloc_arrays_from_search_start
                               amount_needed, hint->inode->i_uid);
 #endif
                quota_ret =
-                   vfs_dq_alloc_block_nodirty(hint->inode, amount_needed);
+                   dquot_alloc_block_nodirty(hint->inode, amount_needed);
                if (quota_ret)  /* Quota exceeded? */
                        return QUOTA_EXCEEDED;
                if (hint->preallocate && hint->prealloc_size) {
@@ -1058,7 +1058,7 @@ static inline int blocknrs_and_prealloc_arrays_from_search_start
                                       "reiserquota: allocating (prealloc) %d blocks id=%u",
                                       hint->prealloc_size, hint->inode->i_uid);
 #endif
-                       quota_ret = vfs_dq_prealloc_block_nodirty(hint->inode,
+                       quota_ret = dquot_prealloc_block_nodirty(hint->inode,
                                                         hint->prealloc_size);
                        if (quota_ret)
                                hint->preallocate = hint->prealloc_size = 0;
@@ -1092,7 +1092,7 @@ static inline int blocknrs_and_prealloc_arrays_from_search_start
                                               hint->inode->i_uid);
 #endif
                                /* Free not allocated blocks */
-                               vfs_dq_free_block_nodirty(hint->inode,
+                               dquot_free_block_nodirty(hint->inode,
                                        amount_needed + hint->prealloc_size -
                                        nr_allocated);
                        }
@@ -1125,7 +1125,7 @@ static inline int blocknrs_and_prealloc_arrays_from_search_start
                               REISERFS_I(hint->inode)->i_prealloc_count,
                               hint->inode->i_uid);
 #endif
-               vfs_dq_free_block_nodirty(hint->inode, amount_needed +
+               dquot_free_block_nodirty(hint->inode, amount_needed +
                                         hint->prealloc_size - nr_allocated -
                                         REISERFS_I(hint->inode)->
                                         i_prealloc_count);
index da2dba082e2d4e6ba5191bf938d134ca647d3877..1d9c12714c5cd4a69384519d4a183447b956d27d 100644 (file)
@@ -289,7 +289,7 @@ const struct file_operations reiserfs_file_operations = {
        .compat_ioctl = reiserfs_compat_ioctl,
 #endif
        .mmap = reiserfs_file_mmap,
-       .open = generic_file_open,
+       .open = dquot_file_open,
        .release = reiserfs_file_release,
        .fsync = reiserfs_sync_file,
        .aio_read = generic_file_aio_read,
index 2df0f5c7c60bf37b69b7949e239e2d81a57b0ab5..d1da94b82d8f77115a714f1cc75ab36f9a84ba6c 100644 (file)
@@ -34,6 +34,9 @@ void reiserfs_delete_inode(struct inode *inode)
        int depth;
        int err;
 
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
+
        truncate_inode_pages(&inode->i_data, 0);
 
        depth = reiserfs_write_lock_once(inode->i_sb);
@@ -54,7 +57,7 @@ void reiserfs_delete_inode(struct inode *inode)
                 * after delete_object so that quota updates go into the same transaction as
                 * stat data deletion */
                if (!err) 
-                       vfs_dq_free_inode(inode);
+                       dquot_free_inode(inode);
 
                if (journal_end(&th, inode->i_sb, jbegin_count))
                        goto out;
@@ -1615,7 +1618,7 @@ int reiserfs_encode_fh(struct dentry *dentry, __u32 * data, int *lenp,
 ** to properly mark inodes for datasync and such, but only actually
 ** does something when called for a synchronous update.
 */
-int reiserfs_write_inode(struct inode *inode, int do_sync)
+int reiserfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        struct reiserfs_transaction_handle th;
        int jbegin_count = 1;
@@ -1627,7 +1630,7 @@ int reiserfs_write_inode(struct inode *inode, int do_sync)
         ** inode needs to reach disk for safety, and they can safely be
         ** ignored because the altered inode has already been logged.
         */
-       if (do_sync && !(current->flags & PF_MEMALLOC)) {
+       if (wbc->sync_mode == WB_SYNC_ALL && !(current->flags & PF_MEMALLOC)) {
                reiserfs_write_lock(inode->i_sb);
                if (!journal_begin(&th, inode->i_sb, jbegin_count)) {
                        reiserfs_update_sd(&th, inode);
@@ -1765,10 +1768,10 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,
 
        BUG_ON(!th->t_trans_id);
 
-       if (vfs_dq_alloc_inode(inode)) {
-               err = -EDQUOT;
+       dquot_initialize(inode);
+       err = dquot_alloc_inode(inode);
+       if (err)
                goto out_end_trans;
-       }
        if (!dir->i_nlink) {
                err = -EPERM;
                goto out_bad_inode;
@@ -1959,12 +1962,12 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th,
        INODE_PKEY(inode)->k_objectid = 0;
 
        /* Quota change must be inside a transaction for journaling */
-       vfs_dq_free_inode(inode);
+       dquot_free_inode(inode);
 
       out_end_trans:
        journal_end(th, th->t_super, th->t_blocks_allocated);
        /* Drop can be outside and it needs more credits so it's better to have it outside */
-       vfs_dq_drop(inode);
+       dquot_drop(inode);
        inode->i_flags |= S_NOQUOTA;
        make_bad_inode(inode);
 
@@ -3073,6 +3076,8 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)
 
        depth = reiserfs_write_lock_once(inode->i_sb);
        if (attr->ia_valid & ATTR_SIZE) {
+               dquot_initialize(inode);
+
                /* version 2 items will be caught by the s_maxbytes check
                 ** done for us in vmtruncate
                 */
@@ -3134,8 +3139,7 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr)
                                                  jbegin_count);
                                if (error)
                                        goto out;
-                               error =
-                                   vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
+                               error = dquot_transfer(inode, attr);
                                if (error) {
                                        journal_end(&th, inode->i_sb,
                                                    jbegin_count);
index 9d4dcf0b07cbdcd65353915de08e9a27e9f99848..96e4cbbfaa1887337660ce65b1795f125dec6623 100644 (file)
@@ -546,7 +546,7 @@ static int reiserfs_add_entry(struct reiserfs_transaction_handle *th,
 */
 static int drop_new_inode(struct inode *inode)
 {
-       vfs_dq_drop(inode);
+       dquot_drop(inode);
        make_bad_inode(inode);
        inode->i_flags |= S_NOQUOTA;
        iput(inode);
@@ -554,7 +554,7 @@ static int drop_new_inode(struct inode *inode)
 }
 
 /* utility function that does setup for reiserfs_new_inode.
-** vfs_dq_init needs lots of credits so it's better to have it
+** dquot_initialize needs lots of credits so it's better to have it
 ** outside of a transaction, so we had to pull some bits of
 ** reiserfs_new_inode out into this func.
 */
@@ -577,7 +577,7 @@ static int new_inode_init(struct inode *inode, struct inode *dir, int mode)
        } else {
                inode->i_gid = current_fsgid();
        }
-       vfs_dq_init(inode);
+       dquot_initialize(inode);
        return 0;
 }
 
@@ -594,6 +594,8 @@ static int reiserfs_create(struct inode *dir, struct dentry *dentry, int mode,
        struct reiserfs_transaction_handle th;
        struct reiserfs_security_handle security;
 
+       dquot_initialize(dir);
+
        if (!(inode = new_inode(dir->i_sb))) {
                return -ENOMEM;
        }
@@ -666,6 +668,8 @@ static int reiserfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
+       dquot_initialize(dir);
+
        if (!(inode = new_inode(dir->i_sb))) {
                return -ENOMEM;
        }
@@ -739,6 +743,8 @@ static int reiserfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
            2 * (REISERFS_QUOTA_INIT_BLOCKS(dir->i_sb) +
                 REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb));
 
+       dquot_initialize(dir);
+
 #ifdef DISPLACE_NEW_PACKING_LOCALITIES
        /* set flag that new packing locality created and new blocks for the content     * of that directory are not displaced yet */
        REISERFS_I(dir)->new_packing_locality = 1;
@@ -842,6 +848,8 @@ static int reiserfs_rmdir(struct inode *dir, struct dentry *dentry)
            JOURNAL_PER_BALANCE_CNT * 2 + 2 +
            4 * REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb);
 
+       dquot_initialize(dir);
+
        reiserfs_write_lock(dir->i_sb);
        retval = journal_begin(&th, dir->i_sb, jbegin_count);
        if (retval)
@@ -923,6 +931,8 @@ static int reiserfs_unlink(struct inode *dir, struct dentry *dentry)
        unsigned long savelink;
        int depth;
 
+       dquot_initialize(dir);
+
        inode = dentry->d_inode;
 
        /* in this transaction we can be doing at max two balancings and update
@@ -1024,6 +1034,8 @@ static int reiserfs_symlink(struct inode *parent_dir,
            2 * (REISERFS_QUOTA_INIT_BLOCKS(parent_dir->i_sb) +
                 REISERFS_QUOTA_TRANS_BLOCKS(parent_dir->i_sb));
 
+       dquot_initialize(parent_dir);
+
        if (!(inode = new_inode(parent_dir->i_sb))) {
                return -ENOMEM;
        }
@@ -1111,6 +1123,8 @@ static int reiserfs_link(struct dentry *old_dentry, struct inode *dir,
            JOURNAL_PER_BALANCE_CNT * 3 +
            2 * REISERFS_QUOTA_TRANS_BLOCKS(dir->i_sb);
 
+       dquot_initialize(dir);
+
        reiserfs_write_lock(dir->i_sb);
        if (inode->i_nlink >= REISERFS_LINK_MAX) {
                //FIXME: sd_nlink is 32 bit for new files
@@ -1235,6 +1249,9 @@ static int reiserfs_rename(struct inode *old_dir, struct dentry *old_dentry,
            JOURNAL_PER_BALANCE_CNT * 3 + 5 +
            4 * REISERFS_QUOTA_TRANS_BLOCKS(old_dir->i_sb);
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        old_inode = old_dentry->d_inode;
        new_dentry_inode = new_dentry->d_inode;
 
index 5fa7118f04e117c079a14b7da3811450f6d72573..313d39d639ebfde26af3116a8b1a510722beaa24 100644 (file)
@@ -1299,7 +1299,7 @@ int reiserfs_delete_item(struct reiserfs_transaction_handle *th,
                       "reiserquota delete_item(): freeing %u, id=%u type=%c",
                       quota_cut_bytes, inode->i_uid, head2type(&s_ih));
 #endif
-       vfs_dq_free_space_nodirty(inode, quota_cut_bytes);
+       dquot_free_space_nodirty(inode, quota_cut_bytes);
 
        /* Return deleted body length */
        return ret_value;
@@ -1383,7 +1383,7 @@ void reiserfs_delete_solid_item(struct reiserfs_transaction_handle *th,
                                               quota_cut_bytes, inode->i_uid,
                                               key2type(key));
 #endif
-                               vfs_dq_free_space_nodirty(inode,
+                               dquot_free_space_nodirty(inode,
                                                         quota_cut_bytes);
                        }
                        break;
@@ -1733,7 +1733,7 @@ int reiserfs_cut_from_item(struct reiserfs_transaction_handle *th,
                       "reiserquota cut_from_item(): freeing %u id=%u type=%c",
                       quota_cut_bytes, inode->i_uid, '?');
 #endif
-       vfs_dq_free_space_nodirty(inode, quota_cut_bytes);
+       dquot_free_space_nodirty(inode, quota_cut_bytes);
        return ret_value;
 }
 
@@ -1968,9 +1968,10 @@ int reiserfs_paste_into_item(struct reiserfs_transaction_handle *th, struct tree
                       key2type(&(key->on_disk_key)));
 #endif
 
-       if (vfs_dq_alloc_space_nodirty(inode, pasted_size)) {
+       retval = dquot_alloc_space_nodirty(inode, pasted_size);
+       if (retval) {
                pathrelse(search_path);
-               return -EDQUOT;
+               return retval;
        }
        init_tb_struct(th, &s_paste_balance, th->t_super, search_path,
                       pasted_size);
@@ -2024,7 +2025,7 @@ int reiserfs_paste_into_item(struct reiserfs_transaction_handle *th, struct tree
                       pasted_size, inode->i_uid,
                       key2type(&(key->on_disk_key)));
 #endif
-       vfs_dq_free_space_nodirty(inode, pasted_size);
+       dquot_free_space_nodirty(inode, pasted_size);
        return retval;
 }
 
@@ -2062,9 +2063,10 @@ int reiserfs_insert_item(struct reiserfs_transaction_handle *th,
 #endif
                /* We can't dirty inode here. It would be immediately written but
                 * appropriate stat item isn't inserted yet... */
-               if (vfs_dq_alloc_space_nodirty(inode, quota_bytes)) {
+               retval = dquot_alloc_space_nodirty(inode, quota_bytes);
+               if (retval) {
                        pathrelse(path);
-                       return -EDQUOT;
+                       return retval;
                }
        }
        init_tb_struct(th, &s_ins_balance, th->t_super, path,
@@ -2113,6 +2115,6 @@ int reiserfs_insert_item(struct reiserfs_transaction_handle *th,
                       quota_bytes, inode->i_uid, head2type(ih));
 #endif
        if (inode)
-               vfs_dq_free_space_nodirty(inode, quota_bytes);
+               dquot_free_space_nodirty(inode, quota_bytes);
        return retval;
 }
index b4a7dd03bdb9b9e49931f7a2fc360f35fbe1a561..04bf5d791bdad8b9fea4c6eecc6d7772035d7144 100644 (file)
@@ -246,7 +246,7 @@ static int finish_unfinished(struct super_block *s)
                        retval = remove_save_link_only(s, &save_link_key, 0);
                        continue;
                }
-               vfs_dq_init(inode);
+               dquot_initialize(inode);
 
                if (truncate && S_ISDIR(inode->i_mode)) {
                        /* We got a truncate request for a dir which is impossible.
@@ -578,6 +578,11 @@ out:
        reiserfs_write_unlock_once(inode->i_sb, lock_depth);
 }
 
+static void reiserfs_clear_inode(struct inode *inode)
+{
+       dquot_drop(inode);
+}
+
 #ifdef CONFIG_QUOTA
 static ssize_t reiserfs_quota_write(struct super_block *, int, const char *,
                                    size_t, loff_t);
@@ -590,6 +595,7 @@ static const struct super_operations reiserfs_sops = {
        .destroy_inode = reiserfs_destroy_inode,
        .write_inode = reiserfs_write_inode,
        .dirty_inode = reiserfs_dirty_inode,
+       .clear_inode = reiserfs_clear_inode,
        .delete_inode = reiserfs_delete_inode,
        .put_super = reiserfs_put_super,
        .write_super = reiserfs_write_super,
@@ -616,13 +622,6 @@ static int reiserfs_write_info(struct super_block *, int);
 static int reiserfs_quota_on(struct super_block *, int, int, char *, int);
 
 static const struct dquot_operations reiserfs_quota_operations = {
-       .initialize = dquot_initialize,
-       .drop = dquot_drop,
-       .alloc_space = dquot_alloc_space,
-       .alloc_inode = dquot_alloc_inode,
-       .free_space = dquot_free_space,
-       .free_inode = dquot_free_inode,
-       .transfer = dquot_transfer,
        .write_dquot = reiserfs_write_dquot,
        .acquire_dquot = reiserfs_acquire_dquot,
        .release_dquot = reiserfs_release_dquot,
index 81f09fab8ae476ccbdb9547d226e397e1b8cb6d1..37d034ca7d994a8c84496dde7d9d88c54a3f441b 100644 (file)
@@ -61,7 +61,6 @@
 static int xattr_create(struct inode *dir, struct dentry *dentry, int mode)
 {
        BUG_ON(!mutex_is_locked(&dir->i_mutex));
-       vfs_dq_init(dir);
        return dir->i_op->create(dir, dentry, mode, NULL);
 }
 #endif
@@ -69,7 +68,6 @@ static int xattr_create(struct inode *dir, struct dentry *dentry, int mode)
 static int xattr_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
        BUG_ON(!mutex_is_locked(&dir->i_mutex));
-       vfs_dq_init(dir);
        return dir->i_op->mkdir(dir, dentry, mode);
 }
 
@@ -81,7 +79,6 @@ static int xattr_unlink(struct inode *dir, struct dentry *dentry)
 {
        int error;
        BUG_ON(!mutex_is_locked(&dir->i_mutex));
-       vfs_dq_init(dir);
 
        reiserfs_mutex_lock_nested_safe(&dentry->d_inode->i_mutex,
                                        I_MUTEX_CHILD, dir->i_sb);
@@ -97,7 +94,6 @@ static int xattr_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int error;
        BUG_ON(!mutex_is_locked(&dir->i_mutex));
-       vfs_dq_init(dir);
 
        reiserfs_mutex_lock_nested_safe(&dentry->d_inode->i_mutex,
                                        I_MUTEX_CHILD, dir->i_sb);
index fd38ce2e32e349ba3c418dbc454957dd0e3ad958..73715e90030f0c78059c98c6a4e0abf6b8a04ffa 100644 (file)
@@ -821,7 +821,7 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
        struct poll_list *walk = head;
        unsigned long todo = nfds;
 
-       if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+       if (nfds > rlimit(RLIMIT_NOFILE))
                return -EINVAL;
 
        len = min_t(unsigned int, nfds, N_STACK_PPS);
index 5afd554efad32cbcb620838178f84ec0b5debc9b..e1f437be6c3c85c288ca3d3b76c15bf409da0f4c 100644 (file)
@@ -734,7 +734,7 @@ EXPORT_SYMBOL(seq_hlist_start_head);
  * seq_hlist_next - move to the next position of the hlist
  * @v:    the current iterator
  * @head: the head of the hlist
- * @pos:  the current posision
+ * @ppos: the current position
  *
  * Called at seq_file->op->next().
  */
@@ -800,7 +800,7 @@ EXPORT_SYMBOL(seq_hlist_start_head_rcu);
  * seq_hlist_next_rcu - move to the next position of the hlist protected by RCU
  * @v:    the current iterator
  * @head: the head of the hlist
- * @pos:  the current posision
+ * @ppos: the current position
  *
  * Called at seq_file->op->next().
  *
index 70e3244fa30f6e67ec14df4f0a8d99ddd4d9719e..df8a19ef870d6db72cca324e4ef4667931b8dbd7 100644 (file)
@@ -4,4 +4,4 @@
 
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
-squashfs-y += namei.o super.o symlink.o
+squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o
index 2a79603103492220c85c8828efba9a6b9e15c66d..1cb0d81b164b5cf02ff67ceedc8275838b6db868 100644 (file)
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
-#include <linux/mutex.h>
 #include <linux/string.h>
 #include <linux/buffer_head.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
+#include "decompressor.h"
 
 /*
  * Read the metadata block length, this is stored in the first two
@@ -153,72 +152,10 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
        }
 
        if (compressed) {
-               int zlib_err = 0, zlib_init = 0;
-
-               /*
-                * Uncompress block.
-                */
-
-               mutex_lock(&msblk->read_data_mutex);
-
-               msblk->stream.avail_out = 0;
-               msblk->stream.avail_in = 0;
-
-               bytes = length;
-               do {
-                       if (msblk->stream.avail_in == 0 && k < b) {
-                               avail = min(bytes, msblk->devblksize - offset);
-                               bytes -= avail;
-                               wait_on_buffer(bh[k]);
-                               if (!buffer_uptodate(bh[k]))
-                                       goto release_mutex;
-
-                               if (avail == 0) {
-                                       offset = 0;
-                                       put_bh(bh[k++]);
-                                       continue;
-                               }
-
-                               msblk->stream.next_in = bh[k]->b_data + offset;
-                               msblk->stream.avail_in = avail;
-                               offset = 0;
-                       }
-
-                       if (msblk->stream.avail_out == 0 && page < pages) {
-                               msblk->stream.next_out = buffer[page++];
-                               msblk->stream.avail_out = PAGE_CACHE_SIZE;
-                       }
-
-                       if (!zlib_init) {
-                               zlib_err = zlib_inflateInit(&msblk->stream);
-                               if (zlib_err != Z_OK) {
-                                       ERROR("zlib_inflateInit returned"
-                                               " unexpected result 0x%x,"
-                                               " srclength %d\n", zlib_err,
-                                               srclength);
-                                       goto release_mutex;
-                               }
-                               zlib_init = 1;
-                       }
-
-                       zlib_err = zlib_inflate(&msblk->stream, Z_SYNC_FLUSH);
-
-                       if (msblk->stream.avail_in == 0 && k < b)
-                               put_bh(bh[k++]);
-               } while (zlib_err == Z_OK);
-
-               if (zlib_err != Z_STREAM_END) {
-                       ERROR("zlib_inflate error, data probably corrupt\n");
-                       goto release_mutex;
-               }
-
-               zlib_err = zlib_inflateEnd(&msblk->stream);
-               if (zlib_err != Z_OK) {
-                       ERROR("zlib_inflate error, data probably corrupt\n");
-                       goto release_mutex;
-               }
-               length = msblk->stream.total_out;
-               mutex_unlock(&msblk->read_data_mutex);
+               length = squashfs_decompress(msblk, buffer, bh, b, offset,
+                        length, srclength, pages);
+               if (length < 0)
+                       goto read_failure;
        } else {
                /*
                 * Block is uncompressed.
@@ -255,9 +192,6 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
        kfree(bh);
        return length;
 
-release_mutex:
-       mutex_unlock(&msblk->read_data_mutex);
-
 block_release:
        for (; k < b; k++)
                put_bh(bh[k]);
index 40c98fa6b5d6a779d94bbb6b937a67a8c053d8c7..57314bee9059709d9230c80dff70210729236c07 100644 (file)
@@ -51,7 +51,6 @@
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
-#include <linux/zlib.h>
 #include <linux/pagemap.h>
 
 #include "squashfs_fs.h"
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
new file mode 100644 (file)
index 0000000..157478d
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * decompressor.c
+ */
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "decompressor.h"
+#include "squashfs.h"
+
+/*
+ * This file (and decompressor.h) implements a decompressor framework for
+ * Squashfs, allowing multiple decompressors to be easily supported
+ */
+
+static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
+       NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
+};
+
+static const struct squashfs_decompressor squashfs_lzo_unsupported_comp_ops = {
+       NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
+};
+
+static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
+       NULL, NULL, NULL, 0, "unknown", 0
+};
+
+static const struct squashfs_decompressor *decompressor[] = {
+       &squashfs_zlib_comp_ops,
+       &squashfs_lzma_unsupported_comp_ops,
+       &squashfs_lzo_unsupported_comp_ops,
+       &squashfs_unknown_comp_ops
+};
+
+
+const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
+{
+       int i;
+
+       for (i = 0; decompressor[i]->id; i++)
+               if (id == decompressor[i]->id)
+                       break;
+
+       return decompressor[i];
+}
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
new file mode 100644 (file)
index 0000000..7425f80
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef DECOMPRESSOR_H
+#define DECOMPRESSOR_H
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * decompressor.h
+ */
+
+struct squashfs_decompressor {
+       void    *(*init)(struct squashfs_sb_info *);
+       void    (*free)(void *);
+       int     (*decompress)(struct squashfs_sb_info *, void **,
+               struct buffer_head **, int, int, int, int, int);
+       int     id;
+       char    *name;
+       int     supported;
+};
+
+static inline void *squashfs_decompressor_init(struct squashfs_sb_info *msblk)
+{
+       return msblk->decompressor->init(msblk);
+}
+
+static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
+       void *s)
+{
+       if (msblk->decompressor)
+               msblk->decompressor->free(s);
+}
+
+static inline int squashfs_decompress(struct squashfs_sb_info *msblk,
+       void **buffer, struct buffer_head **bh, int b, int offset, int length,
+       int srclength, int pages)
+{
+       return msblk->decompressor->decompress(msblk, buffer, bh, b, offset,
+               length, srclength, pages);
+}
+#endif
index 566b0eaed868df557a7e05061cd31b0f160a2b54..12b933ac6585d7d0291608cf3253754223a07fd4 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
index 2b1b8fe5e03765cdb91cc6311176d31a8ea3d054..7f93d5a9ee056648a865ae2e2dbf45a3783ce9bb 100644 (file)
@@ -39,7 +39,6 @@
 #include <linux/vfs.h>
 #include <linux/dcache.h>
 #include <linux/exportfs.h>
-#include <linux/zlib.h>
 #include <linux/slab.h>
 
 #include "squashfs_fs.h"
index 717767d831dfb455c9ff2f4d3695c49b1b857c19..a25c5060bdcb5437726497e8bf43d621f09af599 100644 (file)
@@ -47,7 +47,6 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/mutex.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
index b5a2c15bbbc78b75f8bd2871bb9bdc0c95cdcb87..7c90bbd6879d533a9dc021c8add961ed252d2e84 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
index 3795b837ba284d15055388f692d7077fe9597b2b..b7f64bcd2b707df4e5385c15efe8f553cc70fa1d 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
index 9101dbde39ece6a9ca5d8b47d3344c1f3bdc6879..49daaf669e41809d33b377c40c2f39e0f2e462c7 100644 (file)
@@ -40,7 +40,6 @@
 
 #include <linux/fs.h>
 #include <linux/vfs.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
index 9e398653b22b79a6d7ab26724ab0ab291a98fa91..5266bd8ad932edb3a288217d8992693d649b7112 100644 (file)
@@ -57,7 +57,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/dcache.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
index 0e9feb6adf7e120ccad9efd9815717fd44dbc83c..fe2587af5512edc3947622a2e949c53c4fa4d539 100644 (file)
@@ -51,6 +51,9 @@ extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *,
                                u64, int);
 extern int squashfs_read_table(struct super_block *, void *, u64, int);
 
+/* decompressor.c */
+extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
+
 /* export.c */
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
                                unsigned int);
@@ -71,7 +74,7 @@ extern struct inode *squashfs_iget(struct super_block *, long long,
 extern int squashfs_read_inode(struct inode *, long long);
 
 /*
- * Inodes and files operations
+ * Inodes, files and decompressor operations
  */
 
 /* dir.c */
@@ -88,3 +91,6 @@ extern const struct inode_operations squashfs_dir_inode_ops;
 
 /* symlink.c */
 extern const struct address_space_operations squashfs_symlink_aops;
+
+/* zlib_wrapper.c */
+extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
index 283daafc568eea04e5c3d8fdb191b2c65263a70d..79024245ea00f7fb370e7aa8dbef6819da33854b 100644 (file)
 #define SQUASHFS_MAX_FILE_SIZE         (1LL << \
                                        (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
 
-#define SQUASHFS_MARKER_BYTE           0xff
-
 /* meta index cache */
 #define SQUASHFS_META_INDEXES  (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
 #define SQUASHFS_META_ENTRIES  127
@@ -211,7 +209,9 @@ struct meta_index {
 /*
  * definitions for structures on disk
  */
-#define ZLIB_COMPRESSION        1
+#define ZLIB_COMPRESSION       1
+#define LZMA_COMPRESSION       2
+#define LZO_COMPRESSION                3
 
 struct squashfs_super_block {
        __le32                  s_magic;
index c8c65614dd1c2d4c6eefe4d36a6ab2b4985874ab..2e77dc547e253df03d94f8dfe0a849358810d082 100644 (file)
@@ -52,25 +52,25 @@ struct squashfs_cache_entry {
 };
 
 struct squashfs_sb_info {
-       int                     devblksize;
-       int                     devblksize_log2;
-       struct squashfs_cache   *block_cache;
-       struct squashfs_cache   *fragment_cache;
-       struct squashfs_cache   *read_page;
-       int                     next_meta_index;
-       __le64                  *id_table;
-       __le64                  *fragment_index;
-       unsigned int            *fragment_index_2;
-       struct mutex            read_data_mutex;
-       struct mutex            meta_index_mutex;
-       struct meta_index       *meta_index;
-       z_stream                stream;
-       __le64                  *inode_lookup_table;
-       u64                     inode_table;
-       u64                     directory_table;
-       unsigned int            block_size;
-       unsigned short          block_log;
-       long long               bytes_used;
-       unsigned int            inodes;
+       const struct squashfs_decompressor      *decompressor;
+       int                                     devblksize;
+       int                                     devblksize_log2;
+       struct squashfs_cache                   *block_cache;
+       struct squashfs_cache                   *fragment_cache;
+       struct squashfs_cache                   *read_page;
+       int                                     next_meta_index;
+       __le64                                  *id_table;
+       __le64                                  *fragment_index;
+       struct mutex                            read_data_mutex;
+       struct mutex                            meta_index_mutex;
+       struct meta_index                       *meta_index;
+       void                                    *stream;
+       __le64                                  *inode_lookup_table;
+       u64                                     inode_table;
+       u64                                     directory_table;
+       unsigned int                            block_size;
+       unsigned short                          block_log;
+       long long                               bytes_used;
+       unsigned int                            inodes;
 };
 #endif
index 6c197ef53adda0e8300e416d1a13008fbfe4004a..3550aec2f655ccb1a9a8a04dae5a9bb2df4fdc52 100644 (file)
 #include <linux/pagemap.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/zlib.h>
 #include <linux/magic.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
+#include "decompressor.h"
 
 static struct file_system_type squashfs_fs_type;
 static const struct super_operations squashfs_super_ops;
 
-static int supported_squashfs_filesystem(short major, short minor, short comp)
+static const struct squashfs_decompressor *supported_squashfs_filesystem(short
+       major, short minor, short id)
 {
+       const struct squashfs_decompressor *decompressor;
+
        if (major < SQUASHFS_MAJOR) {
                ERROR("Major/Minor mismatch, older Squashfs %d.%d "
                        "filesystems are unsupported\n", major, minor);
-               return -EINVAL;
+               return NULL;
        } else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
                ERROR("Major/Minor mismatch, trying to mount newer "
                        "%d.%d filesystem\n", major, minor);
                ERROR("Please update your kernel\n");
-               return -EINVAL;
+               return NULL;
        }
 
-       if (comp != ZLIB_COMPRESSION)
-               return -EINVAL;
+       decompressor = squashfs_lookup_decompressor(id);
+       if (!decompressor->supported) {
+               ERROR("Filesystem uses \"%s\" compression. This is not "
+                       "supported\n", decompressor->name);
+               return NULL;
+       }
 
-       return 0;
+       return decompressor;
 }
 
 
@@ -87,13 +94,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
        }
        msblk = sb->s_fs_info;
 
-       msblk->stream.workspace = kmalloc(zlib_inflate_workspacesize(),
-               GFP_KERNEL);
-       if (msblk->stream.workspace == NULL) {
-               ERROR("Failed to allocate zlib workspace\n");
-               goto failure;
-       }
-
        sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
        if (sblk == NULL) {
                ERROR("Failed to allocate squashfs_super_block\n");
@@ -120,25 +120,25 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
        }
 
+       err = -EINVAL;
+
        /* Check it is a SQUASHFS superblock */
        sb->s_magic = le32_to_cpu(sblk->s_magic);
        if (sb->s_magic != SQUASHFS_MAGIC) {
                if (!silent)
                        ERROR("Can't find a SQUASHFS superblock on %s\n",
                                                bdevname(sb->s_bdev, b));
-               err = -EINVAL;
                goto failed_mount;
        }
 
-       /* Check the MAJOR & MINOR versions and compression type */
-       err = supported_squashfs_filesystem(le16_to_cpu(sblk->s_major),
+       /* Check the MAJOR & MINOR versions and lookup compression type */
+       msblk->decompressor = supported_squashfs_filesystem(
+                       le16_to_cpu(sblk->s_major),
                        le16_to_cpu(sblk->s_minor),
                        le16_to_cpu(sblk->compression));
-       if (err < 0)
+       if (msblk->decompressor == NULL)
                goto failed_mount;
 
-       err = -EINVAL;
-
        /*
         * Check if there's xattrs in the filesystem.  These are not
         * supported in this version, so warn that they will be ignored.
@@ -205,6 +205,10 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 
        err = -ENOMEM;
 
+       msblk->stream = squashfs_decompressor_init(msblk);
+       if (msblk->stream == NULL)
+               goto failed_mount;
+
        msblk->block_cache = squashfs_cache_init("metadata",
                        SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
        if (msblk->block_cache == NULL)
@@ -292,17 +296,16 @@ failed_mount:
        squashfs_cache_delete(msblk->block_cache);
        squashfs_cache_delete(msblk->fragment_cache);
        squashfs_cache_delete(msblk->read_page);
+       squashfs_decompressor_free(msblk, msblk->stream);
        kfree(msblk->inode_lookup_table);
        kfree(msblk->fragment_index);
        kfree(msblk->id_table);
-       kfree(msblk->stream.workspace);
        kfree(sb->s_fs_info);
        sb->s_fs_info = NULL;
        kfree(sblk);
        return err;
 
 failure:
-       kfree(msblk->stream.workspace);
        kfree(sb->s_fs_info);
        sb->s_fs_info = NULL;
        return -ENOMEM;
@@ -346,10 +349,10 @@ static void squashfs_put_super(struct super_block *sb)
                squashfs_cache_delete(sbi->block_cache);
                squashfs_cache_delete(sbi->fragment_cache);
                squashfs_cache_delete(sbi->read_page);
+               squashfs_decompressor_free(sbi, sbi->stream);
                kfree(sbi->id_table);
                kfree(sbi->fragment_index);
                kfree(sbi->meta_index);
-               kfree(sbi->stream.workspace);
                kfree(sb->s_fs_info);
                sb->s_fs_info = NULL;
        }
index 83d87880aac837640cbc44cf64252c063cdcad47..e80be2022a7fceebfbd37383c65f3b032b45ed00 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/pagemap.h>
-#include <linux/zlib.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
new file mode 100644 (file)
index 0000000..4dd70e0
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * zlib_wrapper.c
+ */
+
+
+#include <linux/mutex.h>
+#include <linux/buffer_head.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "decompressor.h"
+
+static void *zlib_init(struct squashfs_sb_info *dummy)
+{
+       z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
+       if (stream == NULL)
+               goto failed;
+       stream->workspace = kmalloc(zlib_inflate_workspacesize(),
+               GFP_KERNEL);
+       if (stream->workspace == NULL)
+               goto failed;
+
+       return stream;
+
+failed:
+       ERROR("Failed to allocate zlib workspace\n");
+       kfree(stream);
+       return NULL;
+}
+
+
+static void zlib_free(void *strm)
+{
+       z_stream *stream = strm;
+
+       if (stream)
+               kfree(stream->workspace);
+       kfree(stream);
+}
+
+
+static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
+       struct buffer_head **bh, int b, int offset, int length, int srclength,
+       int pages)
+{
+       int zlib_err = 0, zlib_init = 0;
+       int avail, bytes, k = 0, page = 0;
+       z_stream *stream = msblk->stream;
+
+       mutex_lock(&msblk->read_data_mutex);
+
+       stream->avail_out = 0;
+       stream->avail_in = 0;
+
+       bytes = length;
+       do {
+               if (stream->avail_in == 0 && k < b) {
+                       avail = min(bytes, msblk->devblksize - offset);
+                       bytes -= avail;
+                       wait_on_buffer(bh[k]);
+                       if (!buffer_uptodate(bh[k]))
+                               goto release_mutex;
+
+                       if (avail == 0) {
+                               offset = 0;
+                               put_bh(bh[k++]);
+                               continue;
+                       }
+
+                       stream->next_in = bh[k]->b_data + offset;
+                       stream->avail_in = avail;
+                       offset = 0;
+               }
+
+               if (stream->avail_out == 0 && page < pages) {
+                       stream->next_out = buffer[page++];
+                       stream->avail_out = PAGE_CACHE_SIZE;
+               }
+
+               if (!zlib_init) {
+                       zlib_err = zlib_inflateInit(stream);
+                       if (zlib_err != Z_OK) {
+                               ERROR("zlib_inflateInit returned unexpected "
+                                       "result 0x%x, srclength %d\n",
+                                       zlib_err, srclength);
+                               goto release_mutex;
+                       }
+                       zlib_init = 1;
+               }
+
+               zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH);
+
+               if (stream->avail_in == 0 && k < b)
+                       put_bh(bh[k++]);
+       } while (zlib_err == Z_OK);
+
+       if (zlib_err != Z_STREAM_END) {
+               ERROR("zlib_inflate error, data probably corrupt\n");
+               goto release_mutex;
+       }
+
+       zlib_err = zlib_inflateEnd(stream);
+       if (zlib_err != Z_OK) {
+               ERROR("zlib_inflate error, data probably corrupt\n");
+               goto release_mutex;
+       }
+
+       mutex_unlock(&msblk->read_data_mutex);
+       return stream->total_out;
+
+release_mutex:
+       mutex_unlock(&msblk->read_data_mutex);
+
+       for (; k < b; k++)
+               put_bh(bh[k]);
+
+       return -EIO;
+}
+
+const struct squashfs_decompressor squashfs_zlib_comp_ops = {
+       .init = zlib_init,
+       .free = zlib_free,
+       .decompress = zlib_uncompress,
+       .id = ZLIB_COMPRESSION,
+       .name = "zlib",
+       .supported = 1
+};
+
index aff046b0fe78b8be88b7a81a10f2ed39b38e2f85..f35ac6022109d3a61e1ddb7cabbc67db8d5cf107 100644 (file)
@@ -568,7 +568,7 @@ out:
 int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 {
        int retval;
-       int remount_rw;
+       int remount_rw, remount_ro;
 
        if (sb->s_frozen != SB_UNFROZEN)
                return -EBUSY;
@@ -583,9 +583,12 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
        shrink_dcache_sb(sb);
        sync_filesystem(sb);
 
+       remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY);
+       remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
+
        /* If we are remounting RDONLY and current sb is read/write,
           make sure there are no rw files opened */
-       if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) {
+       if (remount_ro) {
                if (force)
                        mark_files_ro(sb);
                else if (!fs_may_remount_ro(sb))
@@ -594,7 +597,6 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
                if (retval < 0 && retval != -ENOSYS)
                        return -EBUSY;
        }
-       remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
 
        if (sb->s_op->remount_fs) {
                retval = sb->s_op->remount_fs(sb, &flags, data);
@@ -604,6 +606,16 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
        sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
        if (remount_rw)
                vfs_dq_quota_on_remount(sb);
+       /*
+        * Some filesystems modify their metadata via some other path than the
+        * bdev buffer cache (eg. use a private mapping, or directories in
+        * pagecache, etc). Also file data modifications go via their own
+        * mappings. So If we try to mount readonly then copy the filesystem
+        * from bdev, we could get stale data, so invalidate it to give a best
+        * effort at coherency.
+        */
+       if (remount_ro && sb->s_bdev)
+               invalidate_bdev(sb->s_bdev);
        return 0;
 }
 
@@ -925,6 +937,9 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
        if (!mnt)
                goto out;
 
+       if (flags & MS_KERNMOUNT)
+               mnt->mnt_flags = MNT_INTERNAL;
+
        if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
                secdata = alloc_secdata();
                if (!secdata)
index 418727a2a2390f435d68b1fad91dede88dc4c17f..f557d71cb0971692b5337cefad5db272f10b6a78 100644 (file)
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -34,14 +34,14 @@ static int __sync_filesystem(struct super_block *sb, int wait)
        if (!sb->s_bdi)
                return 0;
 
-       /* Avoid doing twice syncing and cache pruning for quota sync */
-       if (!wait) {
-               writeout_quota_sb(sb, -1);
-               writeback_inodes_sb(sb);
-       } else {
-               sync_quota_sb(sb, -1);
+       if (sb->s_qcop && sb->s_qcop->quota_sync)
+               sb->s_qcop->quota_sync(sb, -1, wait);
+
+       if (wait)
                sync_inodes_sb(sb);
-       }
+       else
+               writeback_inodes_sb(sb);
+
        if (sb->s_op->sync_fs)
                sb->s_op->sync_fs(sb, wait);
        return __sync_blockdev(sb->s_bdev, wait);
index 9824743832a7212c4b1386b8127c712f62fa215b..4573734d723dd3dc0a9cdccfd135505ec315930c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/buffer_head.h>
 #include <linux/vfs.h>
+#include <linux/writeback.h>
 #include <linux/namei.h>
 #include <asm/byteorder.h>
 #include "sysv.h"
@@ -246,7 +247,7 @@ bad_inode:
        return ERR_PTR(-EIO);
 }
 
-int sysv_write_inode(struct inode *inode, int wait)
+static int __sysv_write_inode(struct inode *inode, int wait)
 {
        struct super_block * sb = inode->i_sb;
        struct sysv_sb_info * sbi = SYSV_SB(sb);
@@ -296,9 +297,14 @@ int sysv_write_inode(struct inode *inode, int wait)
        return 0;
 }
 
+int sysv_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+       return __sysv_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
 int sysv_sync_inode(struct inode *inode)
 {
-       return sysv_write_inode(inode, 1);
+       return __sysv_write_inode(inode, 1);
 }
 
 static void sysv_delete_inode(struct inode *inode)
index 53786eb5cf60a2770a48ee0856e24683461f449d..94cb9b4d76c28871c7d5dfcdb8869118f1f1e7b8 100644 (file)
@@ -142,7 +142,7 @@ extern int __sysv_write_begin(struct file *file, struct address_space *mapping,
 
 /* inode.c */
 extern struct inode *sysv_iget(struct super_block *, unsigned int);
-extern int sysv_write_inode(struct inode *, int);
+extern int sysv_write_inode(struct inode *, struct writeback_control *wbc);
 extern int sysv_sync_inode(struct inode *);
 extern void sysv_set_inode(struct inode *, dev_t);
 extern int sysv_getattr(struct vfsmount *, struct dentry *, struct kstat *);
index 552fb0111fff0354cab51be0b3fcc5ca11b0c6a0..401e503d44a147da68e4b0a0f2fad7f518949a55 100644 (file)
@@ -1120,7 +1120,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (release)
                ubifs_release_budget(c, &ino_req);
        if (IS_SYNC(old_inode))
-               err = old_inode->i_sb->s_op->write_inode(old_inode, 1);
+               err = old_inode->i_sb->s_op->write_inode(old_inode, NULL);
        return err;
 
 out_cancel:
index 16a6444330ec65b79488ceea7bd82295c4fde5b0..e26c02ab6cd5578ae663e777749e74fa2772111a 100644 (file)
@@ -1011,7 +1011,7 @@ static int ubifs_writepage(struct page *page, struct writeback_control *wbc)
        /* Is the page fully inside @i_size? */
        if (page->index < end_index) {
                if (page->index >= synced_i_size >> PAGE_CACHE_SHIFT) {
-                       err = inode->i_sb->s_op->write_inode(inode, 1);
+                       err = inode->i_sb->s_op->write_inode(inode, NULL);
                        if (err)
                                goto out_unlock;
                        /*
@@ -1039,7 +1039,7 @@ static int ubifs_writepage(struct page *page, struct writeback_control *wbc)
        kunmap_atomic(kaddr, KM_USER0);
 
        if (i_size > synced_i_size) {
-               err = inode->i_sb->s_op->write_inode(inode, 1);
+               err = inode->i_sb->s_op->write_inode(inode, NULL);
                if (err)
                        goto out_unlock;
        }
@@ -1242,7 +1242,7 @@ static int do_setattr(struct ubifs_info *c, struct inode *inode,
        if (release)
                ubifs_release_budget(c, &req);
        if (IS_SYNC(inode))
-               err = inode->i_sb->s_op->write_inode(inode, 1);
+               err = inode->i_sb->s_op->write_inode(inode, NULL);
        return err;
 
 out:
@@ -1316,7 +1316,7 @@ int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync)
         * the inode unless this is a 'datasync()' call.
         */
        if (!datasync || (inode->i_state & I_DIRTY_DATASYNC)) {
-               err = inode->i_sb->s_op->write_inode(inode, 1);
+               err = inode->i_sb->s_op->write_inode(inode, NULL);
                if (err)
                        return err;
        }
index 43f9d19a6f3313ec3df7f358138e153703d4895b..4d2f2157dd3fa9a70a680baea10a68fba7a56061 100644 (file)
@@ -283,7 +283,7 @@ static void ubifs_destroy_inode(struct inode *inode)
 /*
  * Note, Linux write-back code calls this without 'i_mutex'.
  */
-static int ubifs_write_inode(struct inode *inode, int wait)
+static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        int err = 0;
        struct ubifs_info *c = inode->i_sb->s_fs_info;
index 82372e332f08521010183f0ce6c2a674f7912b7f..ccc3ad7242d45862cff17f4e24579acbea125e8e 100644 (file)
@@ -208,7 +208,7 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
                                        ((char *)bh->b_data)[(bit + i) >> 3]);
                        } else {
                                if (inode)
-                                       vfs_dq_free_block(inode, 1);
+                                       dquot_free_block(inode, 1);
                                udf_add_free_space(sb, sbi->s_partition, 1);
                        }
                }
@@ -260,11 +260,11 @@ static int udf_bitmap_prealloc_blocks(struct super_block *sb,
                while (bit < (sb->s_blocksize << 3) && block_count > 0) {
                        if (!udf_test_bit(bit, bh->b_data))
                                goto out;
-                       else if (vfs_dq_prealloc_block(inode, 1))
+                       else if (dquot_prealloc_block(inode, 1))
                                goto out;
                        else if (!udf_clear_bit(bit, bh->b_data)) {
                                udf_debug("bit already cleared for block %d\n", bit);
-                               vfs_dq_free_block(inode, 1);
+                               dquot_free_block(inode, 1);
                                goto out;
                        }
                        block_count--;
@@ -390,10 +390,14 @@ got_block:
        /*
         * Check quota for allocation of this block.
         */
-       if (inode && vfs_dq_alloc_block(inode, 1)) {
-               mutex_unlock(&sbi->s_alloc_mutex);
-               *err = -EDQUOT;
-               return 0;
+       if (inode) {
+               int ret = dquot_alloc_block(inode, 1);
+
+               if (ret) {
+                       mutex_unlock(&sbi->s_alloc_mutex);
+                       *err = ret;
+                       return 0;
+               }
        }
 
        newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) -
@@ -449,7 +453,7 @@ static void udf_table_free_blocks(struct super_block *sb,
        /* We do this up front - There are some error conditions that
           could occure, but.. oh well */
        if (inode)
-               vfs_dq_free_block(inode, count);
+               dquot_free_block(inode, count);
        udf_add_free_space(sb, sbi->s_partition, count);
 
        start = bloc->logicalBlockNum + offset;
@@ -547,7 +551,7 @@ static void udf_table_free_blocks(struct super_block *sb,
                }
 
                if (epos.offset + (2 * adsize) > sb->s_blocksize) {
-                       char *sptr, *dptr;
+                       unsigned char *sptr, *dptr;
                        int loffset;
 
                        brelse(oepos.bh);
@@ -694,7 +698,7 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
                epos.offset -= adsize;
 
                alloc_count = (elen >> sb->s_blocksize_bits);
-               if (inode && vfs_dq_prealloc_block(inode,
+               if (inode && dquot_prealloc_block(inode,
                        alloc_count > block_count ? block_count : alloc_count))
                        alloc_count = 0;
                else if (alloc_count > block_count) {
@@ -797,12 +801,13 @@ static int udf_table_new_block(struct super_block *sb,
        newblock = goal_eloc.logicalBlockNum;
        goal_eloc.logicalBlockNum++;
        goal_elen -= sb->s_blocksize;
-
-       if (inode && vfs_dq_alloc_block(inode, 1)) {
-               brelse(goal_epos.bh);
-               mutex_unlock(&sbi->s_alloc_mutex);
-               *err = -EDQUOT;
-               return 0;
+       if (inode) {
+               *err = dquot_alloc_block(inode, 1);
+               if (*err) {
+                       brelse(goal_epos.bh);
+                       mutex_unlock(&sbi->s_alloc_mutex);
+                       return 0;
+               }
        }
 
        if (goal_elen)
index 61d9a76a3a69dd109e3b3051e9bc89ddaa92990d..f0f2a436251e4cfcbc40ba9e7e3c6ee69dde47e0 100644 (file)
@@ -45,8 +45,8 @@ static int do_udf_readdir(struct inode *dir, struct file *filp,
        int block, iblock;
        loff_t nf_pos = (filp->f_pos - 1) << 2;
        int flen;
-       char *fname = NULL;
-       char *nameptr;
+       unsigned char *fname = NULL;
+       unsigned char *nameptr;
        uint16_t liu;
        uint8_t lfi;
        loff_t size = udf_ext0_offset(dir) + dir->i_size;
index f311d509b6a3541f5f93f81e785dd820b82f7daf..1eb06774ed903b22db3344db2b56404667e5acca 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
 #include <linux/pagemap.h>
+#include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/aio.h>
 
@@ -207,7 +208,7 @@ const struct file_operations udf_file_operations = {
        .read                   = do_sync_read,
        .aio_read               = generic_file_aio_read,
        .ioctl                  = udf_ioctl,
-       .open                   = generic_file_open,
+       .open                   = dquot_file_open,
        .mmap                   = generic_file_mmap,
        .write                  = do_sync_write,
        .aio_write              = udf_file_aio_write,
@@ -217,6 +218,29 @@ const struct file_operations udf_file_operations = {
        .llseek                 = generic_file_llseek,
 };
 
+static int udf_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+       struct inode *inode = dentry->d_inode;
+       int error;
+
+       error = inode_change_ok(inode, iattr);
+       if (error)
+               return error;
+
+       if (iattr->ia_valid & ATTR_SIZE)
+               dquot_initialize(inode);
+
+       if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
+            (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {
+               error = dquot_transfer(inode, iattr);
+               if (error)
+                       return error;
+       }
+
+       return inode_setattr(inode, iattr);
+}
+
 const struct inode_operations udf_file_inode_operations = {
-       .truncate = udf_truncate,
+       .truncate               = udf_truncate,
+       .setattr                = udf_setattr,
 };
index c10fa39f97e2e7dacd56ff39c13365140dc8f577..fb68c9cd0c3e59feb556b02593aafd332dbb814b 100644 (file)
@@ -36,8 +36,8 @@ void udf_free_inode(struct inode *inode)
         * Note: we must free any quota before locking the superblock,
         * as writing the quota to disk may need the lock as well.
         */
-       vfs_dq_free_inode(inode);
-       vfs_dq_drop(inode);
+       dquot_free_inode(inode);
+       dquot_drop(inode);
 
        clear_inode(inode);
 
@@ -61,7 +61,7 @@ struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
        struct super_block *sb = dir->i_sb;
        struct udf_sb_info *sbi = UDF_SB(sb);
        struct inode *inode;
-       int block;
+       int block, ret;
        uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
        struct udf_inode_info *iinfo;
        struct udf_inode_info *dinfo = UDF_I(dir);
@@ -153,12 +153,14 @@ struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
        insert_inode_hash(inode);
        mark_inode_dirty(inode);
 
-       if (vfs_dq_alloc_inode(inode)) {
-               vfs_dq_drop(inode);
+       dquot_initialize(inode);
+       ret = dquot_alloc_inode(inode);
+       if (ret) {
+               dquot_drop(inode);
                inode->i_flags |= S_NOQUOTA;
                inode->i_nlink = 0;
                iput(inode);
-               *err = -EDQUOT;
+               *err = ret;
                return NULL;
        }
 
index f90231eb29165785d14369191a00fc458dafbc9c..b57ab0402d8971bfd1770c083381c1eabb7c115b 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/pagemap.h>
 #include <linux/buffer_head.h>
 #include <linux/writeback.h>
+#include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/crc-itu-t.h>
 
@@ -70,6 +71,9 @@ static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
 
 void udf_delete_inode(struct inode *inode)
 {
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
+
        truncate_inode_pages(&inode->i_data, 0);
 
        if (is_bad_inode(inode))
@@ -108,6 +112,8 @@ void udf_clear_inode(struct inode *inode)
                        (unsigned long long)inode->i_size,
                        (unsigned long long)iinfo->i_lenExtents);
        }
+
+       dquot_drop(inode);
        kfree(iinfo->i_ext.i_data);
        iinfo->i_ext.i_data = NULL;
 }
@@ -1373,12 +1379,12 @@ static mode_t udf_convert_permissions(struct fileEntry *fe)
        return mode;
 }
 
-int udf_write_inode(struct inode *inode, int sync)
+int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        int ret;
 
        lock_kernel();
-       ret = udf_update_inode(inode, sync);
+       ret = udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
        unlock_kernel();
 
        return ret;
@@ -1672,7 +1678,7 @@ int8_t udf_add_aext(struct inode *inode, struct extent_position *epos,
                return -1;
 
        if (epos->offset + (2 * adsize) > inode->i_sb->s_blocksize) {
-               char *sptr, *dptr;
+               unsigned char *sptr, *dptr;
                struct buffer_head *nbh;
                int err, loffset;
                struct kernel_lb_addr obloc = epos->block;
index cd2115060fdcc728aeefc7a21e80f6fc6af9c28c..db423ab078b1beeaf67316050b826928c9651143 100644 (file)
@@ -34,8 +34,8 @@
 #include <linux/crc-itu-t.h>
 #include <linux/exportfs.h>
 
-static inline int udf_match(int len1, const char *name1, int len2,
-                           const char *name2)
+static inline int udf_match(int len1, const unsigned char *name1, int len2,
+                           const unsigned char *name2)
 {
        if (len1 != len2)
                return 0;
@@ -142,15 +142,15 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
 }
 
 static struct fileIdentDesc *udf_find_entry(struct inode *dir,
-                                           struct qstr *child,
+                                           const struct qstr *child,
                                            struct udf_fileident_bh *fibh,
                                            struct fileIdentDesc *cfi)
 {
        struct fileIdentDesc *fi = NULL;
        loff_t f_pos;
        int block, flen;
-       char *fname = NULL;
-       char *nameptr;
+       unsigned char *fname = NULL;
+       unsigned char *nameptr;
        uint8_t lfi;
        uint16_t liu;
        loff_t size;
@@ -308,7 +308,7 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir,
 {
        struct super_block *sb = dir->i_sb;
        struct fileIdentDesc *fi = NULL;
-       char *name = NULL;
+       unsigned char *name = NULL;
        int namelen;
        loff_t f_pos;
        loff_t size = udf_ext0_offset(dir) + dir->i_size;
@@ -563,6 +563,8 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode,
        int err;
        struct udf_inode_info *iinfo;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        inode = udf_new_inode(dir, mode, &err);
        if (!inode) {
@@ -616,6 +618,8 @@ static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
        if (!old_valid_dev(rdev))
                return -EINVAL;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        err = -EIO;
        inode = udf_new_inode(dir, mode, &err);
@@ -662,6 +666,8 @@ static int udf_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        struct udf_inode_info *dinfo = UDF_I(dir);
        struct udf_inode_info *iinfo;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        err = -EMLINK;
        if (dir->i_nlink >= (256 << sizeof(dir->i_nlink)) - 1)
@@ -799,6 +805,8 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry)
        struct fileIdentDesc *fi, cfi;
        struct kernel_lb_addr tloc;
 
+       dquot_initialize(dir);
+
        retval = -ENOENT;
        lock_kernel();
        fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
@@ -845,6 +853,8 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry)
        struct fileIdentDesc cfi;
        struct kernel_lb_addr tloc;
 
+       dquot_initialize(dir);
+
        retval = -ENOENT;
        lock_kernel();
        fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
@@ -885,20 +895,22 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
 {
        struct inode *inode;
        struct pathComponent *pc;
-       char *compstart;
+       const char *compstart;
        struct udf_fileident_bh fibh;
        struct extent_position epos = {};
        int eoffset, elen = 0;
        struct fileIdentDesc *fi;
        struct fileIdentDesc cfi;
-       char *ea;
+       uint8_t *ea;
        int err;
        int block;
-       char *name = NULL;
+       unsigned char *name = NULL;
        int namelen;
        struct buffer_head *bh;
        struct udf_inode_info *iinfo;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        inode = udf_new_inode(dir, S_IFLNK, &err);
        if (!inode)
@@ -970,7 +982,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
 
                pc = (struct pathComponent *)(ea + elen);
 
-               compstart = (char *)symname;
+               compstart = symname;
 
                do {
                        symname++;
@@ -1069,6 +1081,8 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir,
        int err;
        struct buffer_head *bh;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        if (inode->i_nlink >= (256 << sizeof(inode->i_nlink)) - 1) {
                unlock_kernel();
@@ -1131,6 +1145,9 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct kernel_lb_addr tloc;
        struct udf_inode_info *old_iinfo = UDF_I(old_inode);
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        lock_kernel();
        ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
        if (ofi) {
index c3265e1385d43c5c6ed0e2960cf9d83a72493e4a..852e91845688f24be5b1fa464b70a4d65a044b2a 100644 (file)
 #include <linux/buffer_head.h>
 #include "udf_i.h"
 
-static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen,
-                          char *to)
+static void udf_pc_to_char(struct super_block *sb, unsigned char *from,
+                          int fromlen, unsigned char *to)
 {
        struct pathComponent *pc;
        int elen = 0;
-       char *p = to;
+       unsigned char *p = to;
 
        while (elen < fromlen) {
                pc = (struct pathComponent *)(from + elen);
@@ -75,9 +75,9 @@ static int udf_symlink_filler(struct file *file, struct page *page)
 {
        struct inode *inode = page->mapping->host;
        struct buffer_head *bh = NULL;
-       char *symlink;
+       unsigned char *symlink;
        int err = -EIO;
-       char *p = kmap(page);
+       unsigned char *p = kmap(page);
        struct udf_inode_info *iinfo;
 
        lock_kernel();
index 8d46f4294ee740bd542b3ce1d8565c82c6df5bbb..4223ac855da944d5e10e1e86800ab93c43c20d8d 100644 (file)
@@ -142,7 +142,7 @@ extern void udf_truncate(struct inode *);
 extern void udf_read_inode(struct inode *);
 extern void udf_delete_inode(struct inode *);
 extern void udf_clear_inode(struct inode *);
-extern int udf_write_inode(struct inode *, int);
+extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
 extern long udf_block_map(struct inode *, sector_t);
 extern int udf_extend_file(struct inode *, struct extent_position *,
                           struct kernel_long_ad *, sector_t);
index 54c16ec95dfff52ff9113f0b61ab50d602bd58f9..5cfa4d85ccf23b787b58838bbfde2a9f260fda23 100644 (file)
@@ -85,7 +85,7 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count)
                                   "bit already cleared for fragment %u", i);
        }
        
-       vfs_dq_free_block(inode, count);
+       dquot_free_block(inode, count);
 
        
        fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
@@ -195,7 +195,7 @@ do_more:
                ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
                if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
                        ufs_clusteracct (sb, ucpi, blkno, 1);
-               vfs_dq_free_block(inode, uspi->s_fpb);
+               dquot_free_block(inode, uspi->s_fpb);
 
                fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
                uspi->cs_total.cs_nbfree++;
@@ -511,6 +511,7 @@ static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
        struct ufs_cg_private_info * ucpi;
        struct ufs_cylinder_group * ucg;
        unsigned cgno, fragno, fragoff, count, fragsize, i;
+       int ret;
        
        UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
             (unsigned long long)fragment, oldcount, newcount);
@@ -556,8 +557,9 @@ static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
                fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
        for (i = oldcount; i < newcount; i++)
                ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
-       if (vfs_dq_alloc_block(inode, count)) {
-               *err = -EDQUOT;
+       ret = dquot_alloc_block(inode, count);
+       if (ret) {
+               *err = ret;
                return 0;
        }
 
@@ -596,6 +598,7 @@ static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
        struct ufs_cylinder_group * ucg;
        unsigned oldcg, i, j, k, allocsize;
        u64 result;
+       int ret;
        
        UFSD("ENTER, ino %lu, cgno %u, goal %llu, count %u\n",
             inode->i_ino, cgno, (unsigned long long)goal, count);
@@ -664,7 +667,7 @@ cg_found:
                for (i = count; i < uspi->s_fpb; i++)
                        ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
                i = uspi->s_fpb - count;
-               vfs_dq_free_block(inode, i);
+               dquot_free_block(inode, i);
 
                fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
                uspi->cs_total.cs_nffree += i;
@@ -676,8 +679,9 @@ cg_found:
        result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
        if (result == INVBLOCK)
                return 0;
-       if (vfs_dq_alloc_block(inode, count)) {
-               *err = -EDQUOT;
+       ret = dquot_alloc_block(inode, count);
+       if (ret) {
+               *err = ret;
                return 0;
        }
        for (i = 0; i < count; i++)
@@ -714,6 +718,7 @@ static u64 ufs_alloccg_block(struct inode *inode,
        struct ufs_super_block_first * usb1;
        struct ufs_cylinder_group * ucg;
        u64 result, blkno;
+       int ret;
 
        UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
 
@@ -747,8 +752,9 @@ gotit:
        ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
        if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
                ufs_clusteracct (sb, ucpi, blkno, -1);
-       if (vfs_dq_alloc_block(inode, uspi->s_fpb)) {
-               *err = -EDQUOT;
+       ret = dquot_alloc_block(inode, uspi->s_fpb);
+       if (ret) {
+               *err = ret;
                return INVBLOCK;
        }
 
index 22af68f8b6825e4be3343ecb79ed4dbec587f61b..317a0d444f6b32e82f79a80c90900e67a3da7f08 100644 (file)
@@ -31,7 +31,7 @@
  * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
  */
 static inline int ufs_match(struct super_block *sb, int len,
-               const char * const name, struct ufs_dir_entry * de)
+               const unsigned char *name, struct ufs_dir_entry *de)
 {
        if (len != ufs_get_de_namlen(sb, de))
                return 0;
@@ -70,7 +70,7 @@ static inline unsigned long ufs_dir_pages(struct inode *inode)
        return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
 }
 
-ino_t ufs_inode_by_name(struct inode *dir, struct qstr *qstr)
+ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr)
 {
        ino_t res = 0;
        struct ufs_dir_entry *de;
@@ -249,11 +249,11 @@ struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p)
  * (as a parameter - res_dir). Page is returned mapped and unlocked.
  * Entry is guaranteed to be valid.
  */
-struct ufs_dir_entry *ufs_find_entry(struct inode *dir, struct qstr *qstr,
+struct ufs_dir_entry *ufs_find_entry(struct inode *dir, const struct qstr *qstr,
                                     struct page **res_page)
 {
        struct super_block *sb = dir->i_sb;
-       const char *name = qstr->name;
+       const unsigned char *name = qstr->name;
        int namelen = qstr->len;
        unsigned reclen = UFS_DIR_REC_LEN(namelen);
        unsigned long start, n;
@@ -313,7 +313,7 @@ found:
 int ufs_add_link(struct dentry *dentry, struct inode *inode)
 {
        struct inode *dir = dentry->d_parent->d_inode;
-       const char *name = dentry->d_name.name;
+       const unsigned char *name = dentry->d_name.name;
        int namelen = dentry->d_name.len;
        struct super_block *sb = dir->i_sb;
        unsigned reclen = UFS_DIR_REC_LEN(namelen);
index 73655c61240a0bb756ad4af3d0b841f04c709ae1..a8962cecde5bd6645c13525d4fa0fd8106f5d6dc 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -40,7 +41,7 @@ const struct file_operations ufs_file_operations = {
        .write          = do_sync_write,
        .aio_write      = generic_file_aio_write,
        .mmap           = generic_file_mmap,
-       .open           = generic_file_open,
+       .open           = dquot_file_open,
        .fsync          = simple_fsync,
        .splice_read    = generic_file_splice_read,
 };
index 3527c00fef0d7ebfc8a3df39a96618073ff6d883..230ecf6080263144974e7a886c1f74a6dd219290 100644 (file)
@@ -95,8 +95,8 @@ void ufs_free_inode (struct inode * inode)
 
        is_directory = S_ISDIR(inode->i_mode);
 
-       vfs_dq_free_inode(inode);
-       vfs_dq_drop(inode);
+       dquot_free_inode(inode);
+       dquot_drop(inode);
 
        clear_inode (inode);
 
@@ -355,9 +355,10 @@ cg_found:
 
        unlock_super (sb);
 
-       if (vfs_dq_alloc_inode(inode)) {
-               vfs_dq_drop(inode);
-               err = -EDQUOT;
+       dquot_initialize(inode);
+       err = dquot_alloc_inode(inode);
+       if (err) {
+               dquot_drop(inode);
                goto fail_without_unlock;
        }
 
index 7cf33379fd467825d91957befaab253bcc78f4a1..80b68c3702d129bffeaddbdd388b7ad50ca74e4b 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/mm.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
+#include <linux/writeback.h>
+#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -890,11 +892,11 @@ static int ufs_update_inode(struct inode * inode, int do_sync)
        return 0;
 }
 
-int ufs_write_inode (struct inode * inode, int wait)
+int ufs_write_inode(struct inode *inode, struct writeback_control *wbc)
 {
        int ret;
        lock_kernel();
-       ret = ufs_update_inode (inode, wait);
+       ret = ufs_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
        unlock_kernel();
        return ret;
 }
@@ -908,6 +910,9 @@ void ufs_delete_inode (struct inode * inode)
 {
        loff_t old_i_size;
 
+       if (!is_bad_inode(inode))
+               dquot_initialize(inode);
+
        truncate_inode_pages(&inode->i_data, 0);
        if (is_bad_inode(inode))
                goto no_delete;
index 4c26d9e8bc94f8f24578495985e14202cce5be8e..118556243e7ab60bd2f58aa1f809ee26336f107e 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/time.h>
 #include <linux/fs.h>
 #include <linux/smp_lock.h>
+#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -84,6 +85,9 @@ static int ufs_create (struct inode * dir, struct dentry * dentry, int mode,
        int err;
 
        UFSD("BEGIN\n");
+
+       dquot_initialize(dir);
+
        inode = ufs_new_inode(dir, mode);
        err = PTR_ERR(inode);
 
@@ -107,6 +111,9 @@ static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t
 
        if (!old_valid_dev(rdev))
                return -EINVAL;
+
+       dquot_initialize(dir);
+
        inode = ufs_new_inode(dir, mode);
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
@@ -131,6 +138,8 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
        if (l > sb->s_blocksize)
                goto out_notlocked;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO);
        err = PTR_ERR(inode);
@@ -176,6 +185,8 @@ static int ufs_link (struct dentry * old_dentry, struct inode * dir,
                return -EMLINK;
        }
 
+       dquot_initialize(dir);
+
        inode->i_ctime = CURRENT_TIME_SEC;
        inode_inc_link_count(inode);
        atomic_inc(&inode->i_count);
@@ -193,6 +204,8 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        if (dir->i_nlink >= UFS_LINK_MAX)
                goto out;
 
+       dquot_initialize(dir);
+
        lock_kernel();
        inode_inc_link_count(dir);
 
@@ -237,6 +250,8 @@ static int ufs_unlink(struct inode *dir, struct dentry *dentry)
        struct page *page;
        int err = -ENOENT;
 
+       dquot_initialize(dir);
+
        de = ufs_find_entry(dir, &dentry->d_name, &page);
        if (!de)
                goto out;
@@ -281,6 +296,9 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct ufs_dir_entry *old_de;
        int err = -ENOENT;
 
+       dquot_initialize(old_dir);
+       dquot_initialize(new_dir);
+
        old_de = ufs_find_entry(old_dir, &old_dentry->d_name, &old_page);
        if (!old_de)
                goto out;
index 143c20bfb04b084a73f1814a23ed2b69a3614e2d..66b63a7516159f766d7c4822e9770fce2bf94105 100644 (file)
@@ -1432,6 +1432,11 @@ static void destroy_inodecache(void)
        kmem_cache_destroy(ufs_inode_cachep);
 }
 
+static void ufs_clear_inode(struct inode *inode)
+{
+       dquot_drop(inode);
+}
+
 #ifdef CONFIG_QUOTA
 static ssize_t ufs_quota_read(struct super_block *, int, char *,size_t, loff_t);
 static ssize_t ufs_quota_write(struct super_block *, int, const char *, size_t, loff_t);
@@ -1442,6 +1447,7 @@ static const struct super_operations ufs_super_ops = {
        .destroy_inode  = ufs_destroy_inode,
        .write_inode    = ufs_write_inode,
        .delete_inode   = ufs_delete_inode,
+       .clear_inode    = ufs_clear_inode,
        .put_super      = ufs_put_super,
        .write_super    = ufs_write_super,
        .sync_fs        = ufs_sync_fs,
index 41dd431ce228e409d9efdbbf63865feea608625c..d3b6270cb377ad2afb48184dd379aff63f8c2143 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/buffer_head.h>
 #include <linux/blkdev.h>
 #include <linux/sched.h>
+#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -517,9 +518,18 @@ static int ufs_setattr(struct dentry *dentry, struct iattr *attr)
        if (error)
                return error;
 
+       if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+           (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
+               error = dquot_transfer(inode, attr);
+               if (error)
+                       return error;
+       }
        if (ia_valid & ATTR_SIZE &&
            attr->ia_size != i_size_read(inode)) {
                loff_t old_i_size = inode->i_size;
+
+               dquot_initialize(inode);
+
                error = vmtruncate(inode, attr->ia_size);
                if (error)
                        return error;
index 0b4c39bc0d9ef716b3a81b9ee44dc09f87f48e57..43f9f5d5670eba80cfd01a89407b1da246d0c757 100644 (file)
@@ -86,9 +86,9 @@ extern void ufs_put_cylinder (struct super_block *, unsigned);
 /* dir.c */
 extern const struct inode_operations ufs_dir_inode_operations;
 extern int ufs_add_link (struct dentry *, struct inode *);
-extern ino_t ufs_inode_by_name(struct inode *, struct qstr *);
+extern ino_t ufs_inode_by_name(struct inode *, const struct qstr *);
 extern int ufs_make_empty(struct inode *, struct inode *);
-extern struct ufs_dir_entry *ufs_find_entry(struct inode *, struct qstr *, struct page **);
+extern struct ufs_dir_entry *ufs_find_entry(struct inode *, const struct qstr *, struct page **);
 extern int ufs_delete_entry(struct inode *, struct ufs_dir_entry *, struct page *);
 extern int ufs_empty_dir (struct inode *);
 extern struct ufs_dir_entry *ufs_dotdot(struct inode *, struct page **);
@@ -106,7 +106,7 @@ extern struct inode * ufs_new_inode (struct inode *, int);
 
 /* inode.c */
 extern struct inode *ufs_iget(struct super_block *, unsigned long);
-extern int ufs_write_inode (struct inode *, int);
+extern int ufs_write_inode (struct inode *, struct writeback_control *);
 extern int ufs_sync_inode (struct inode *);
 extern void ufs_delete_inode (struct inode *);
 extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *);
index 5c5a366aa332ca656e4594ee7dfd21a2182d4e92..b4769e40e8bc6b409afe5e81ec5adc1b755511a0 100644 (file)
@@ -105,7 +105,6 @@ xfs-y                               += $(addprefix $(XFS_LINUX)/, \
                                   xfs_globals.o \
                                   xfs_ioctl.o \
                                   xfs_iops.o \
-                                  xfs_lrw.o \
                                   xfs_super.o \
                                   xfs_sync.o \
                                   xfs_xattr.o)
index 66abe36c1213e60ae49e483c82193c72a0d57758..9083357f9e444170b5b95124e936bb0e0d430c7f 100644 (file)
@@ -39,6 +39,7 @@
 #include "xfs_iomap.h"
 #include "xfs_vnodeops.h"
 #include "xfs_trace.h"
+#include "xfs_bmap.h"
 #include <linux/mpage.h>
 #include <linux/pagevec.h>
 #include <linux/writeback.h>
@@ -163,14 +164,17 @@ xfs_ioend_new_eof(
 }
 
 /*
- * Update on-disk file size now that data has been written to disk.
- * The current in-memory file size is i_size.  If a write is beyond
- * eof i_new_size will be the intended file size until i_size is
- * updated.  If this write does not extend all the way to the valid
- * file size then restrict this update to the end of the write.
+ * Update on-disk file size now that data has been written to disk.  The
+ * current in-memory file size is i_size.  If a write is beyond eof i_new_size
+ * will be the intended file size until i_size is updated.  If this write does
+ * not extend all the way to the valid file size then restrict this update to
+ * the end of the write.
+ *
+ * This function does not block as blocking on the inode lock in IO completion
+ * can lead to IO completion order dependency deadlocks.. If it can't get the
+ * inode ilock it will return EAGAIN. Callers must handle this.
  */
-
-STATIC void
+STATIC int
 xfs_setfilesize(
        xfs_ioend_t             *ioend)
 {
@@ -181,16 +185,40 @@ xfs_setfilesize(
        ASSERT(ioend->io_type != IOMAP_READ);
 
        if (unlikely(ioend->io_error))
-               return;
+               return 0;
+
+       if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL))
+               return EAGAIN;
 
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
        isize = xfs_ioend_new_eof(ioend);
        if (isize) {
                ip->i_d.di_size = isize;
-               xfs_mark_inode_dirty_sync(ip);
+               xfs_mark_inode_dirty(ip);
        }
 
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return 0;
+}
+
+/*
+ * Schedule IO completion handling on a xfsdatad if this was
+ * the final hold on this ioend. If we are asked to wait,
+ * flush the workqueue.
+ */
+STATIC void
+xfs_finish_ioend(
+       xfs_ioend_t     *ioend,
+       int             wait)
+{
+       if (atomic_dec_and_test(&ioend->io_remaining)) {
+               struct workqueue_struct *wq;
+
+               wq = (ioend->io_type == IOMAP_UNWRITTEN) ?
+                       xfsconvertd_workqueue : xfsdatad_workqueue;
+               queue_work(wq, &ioend->io_work);
+               if (wait)
+                       flush_workqueue(wq);
+       }
 }
 
 /*
@@ -198,11 +226,11 @@ xfs_setfilesize(
  */
 STATIC void
 xfs_end_io(
-       struct work_struct      *work)
+       struct work_struct *work)
 {
-       xfs_ioend_t             *ioend =
-               container_of(work, xfs_ioend_t, io_work);
-       struct xfs_inode        *ip = XFS_I(ioend->io_inode);
+       xfs_ioend_t     *ioend = container_of(work, xfs_ioend_t, io_work);
+       struct xfs_inode *ip = XFS_I(ioend->io_inode);
+       int             error = 0;
 
        /*
         * For unwritten extents we need to issue transactions to convert a
@@ -210,7 +238,6 @@ xfs_end_io(
         */
        if (ioend->io_type == IOMAP_UNWRITTEN &&
            likely(!ioend->io_error && !XFS_FORCED_SHUTDOWN(ip->i_mount))) {
-               int error;
 
                error = xfs_iomap_write_unwritten(ip, ioend->io_offset,
                                                 ioend->io_size);
@@ -222,30 +249,23 @@ xfs_end_io(
         * We might have to update the on-disk file size after extending
         * writes.
         */
-       if (ioend->io_type != IOMAP_READ)
-               xfs_setfilesize(ioend);
-       xfs_destroy_ioend(ioend);
-}
-
-/*
- * Schedule IO completion handling on a xfsdatad if this was
- * the final hold on this ioend. If we are asked to wait,
- * flush the workqueue.
- */
-STATIC void
-xfs_finish_ioend(
-       xfs_ioend_t     *ioend,
-       int             wait)
-{
-       if (atomic_dec_and_test(&ioend->io_remaining)) {
-               struct workqueue_struct *wq;
-
-               wq = (ioend->io_type == IOMAP_UNWRITTEN) ?
-                       xfsconvertd_workqueue : xfsdatad_workqueue;
-               queue_work(wq, &ioend->io_work);
-               if (wait)
-                       flush_workqueue(wq);
+       if (ioend->io_type != IOMAP_READ) {
+               error = xfs_setfilesize(ioend);
+               ASSERT(!error || error == EAGAIN);
        }
+
+       /*
+        * If we didn't complete processing of the ioend, requeue it to the
+        * tail of the workqueue for another attempt later. Otherwise destroy
+        * it.
+        */
+       if (error == EAGAIN) {
+               atomic_inc(&ioend->io_remaining);
+               xfs_finish_ioend(ioend, 0);
+               /* ensure we don't spin on blocked ioends */
+               delay(1);
+       } else
+               xfs_destroy_ioend(ioend);
 }
 
 /*
@@ -341,7 +361,7 @@ xfs_submit_ioend_bio(
         * but don't update the inode size until I/O completion.
         */
        if (xfs_ioend_new_eof(ioend))
-               xfs_mark_inode_dirty_sync(XFS_I(ioend->io_inode));
+               xfs_mark_inode_dirty(XFS_I(ioend->io_inode));
 
        submit_bio(wbc->sync_mode == WB_SYNC_ALL ?
                   WRITE_SYNC_PLUG : WRITE, bio);
@@ -874,6 +894,118 @@ xfs_cluster_write(
        }
 }
 
+STATIC void
+xfs_vm_invalidatepage(
+       struct page             *page,
+       unsigned long           offset)
+{
+       trace_xfs_invalidatepage(page->mapping->host, page, offset);
+       block_invalidatepage(page, offset);
+}
+
+/*
+ * If the page has delalloc buffers on it, we need to punch them out before we
+ * invalidate the page. If we don't, we leave a stale delalloc mapping on the
+ * inode that can trip a BUG() in xfs_get_blocks() later on if a direct IO read
+ * is done on that same region - the delalloc extent is returned when none is
+ * supposed to be there.
+ *
+ * We prevent this by truncating away the delalloc regions on the page before
+ * invalidating it. Because they are delalloc, we can do this without needing a
+ * transaction. Indeed - if we get ENOSPC errors, we have to be able to do this
+ * truncation without a transaction as there is no space left for block
+ * reservation (typically why we see a ENOSPC in writeback).
+ *
+ * This is not a performance critical path, so for now just do the punching a
+ * buffer head at a time.
+ */
+STATIC void
+xfs_aops_discard_page(
+       struct page             *page)
+{
+       struct inode            *inode = page->mapping->host;
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct buffer_head      *bh, *head;
+       loff_t                  offset = page_offset(page);
+       ssize_t                 len = 1 << inode->i_blkbits;
+
+       if (!xfs_is_delayed_page(page, IOMAP_DELAY))
+               goto out_invalidate;
+
+       xfs_fs_cmn_err(CE_ALERT, ip->i_mount,
+               "page discard on page %p, inode 0x%llx, offset %llu.",
+                       page, ip->i_ino, offset);
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       bh = head = page_buffers(page);
+       do {
+               int             done;
+               xfs_fileoff_t   offset_fsb;
+               xfs_bmbt_irec_t imap;
+               int             nimaps = 1;
+               int             error;
+               xfs_fsblock_t   firstblock;
+               xfs_bmap_free_t flist;
+
+               if (!buffer_delay(bh))
+                       goto next_buffer;
+
+               offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
+
+               /*
+                * Map the range first and check that it is a delalloc extent
+                * before trying to unmap the range. Otherwise we will be
+                * trying to remove a real extent (which requires a
+                * transaction) or a hole, which is probably a bad idea...
+                */
+               error = xfs_bmapi(NULL, ip, offset_fsb, 1,
+                               XFS_BMAPI_ENTIRE,  NULL, 0, &imap,
+                               &nimaps, NULL, NULL);
+
+               if (error) {
+                       /* something screwed, just bail */
+                       xfs_fs_cmn_err(CE_ALERT, ip->i_mount,
+                       "page discard failed delalloc mapping lookup.");
+                       break;
+               }
+               if (!nimaps) {
+                       /* nothing there */
+                       goto next_buffer;
+               }
+               if (imap.br_startblock != DELAYSTARTBLOCK) {
+                       /* been converted, ignore */
+                       goto next_buffer;
+               }
+               WARN_ON(imap.br_blockcount == 0);
+
+               /*
+                * Note: while we initialise the firstblock/flist pair, they
+                * should never be used because blocks should never be
+                * allocated or freed for a delalloc extent and hence we need
+                * don't cancel or finish them after the xfs_bunmapi() call.
+                */
+               xfs_bmap_init(&flist, &firstblock);
+               error = xfs_bunmapi(NULL, ip, offset_fsb, 1, 0, 1, &firstblock,
+                                       &flist, NULL, &done);
+
+               ASSERT(!flist.xbf_count && !flist.xbf_first);
+               if (error) {
+                       /* something screwed, just bail */
+                       xfs_fs_cmn_err(CE_ALERT, ip->i_mount,
+                       "page discard unable to remove delalloc mapping.");
+                       break;
+               }
+next_buffer:
+               offset += len;
+
+       } while ((bh = bh->b_this_page) != head);
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out_invalidate:
+       xfs_vm_invalidatepage(page, 0);
+       return;
+}
+
 /*
  * Calling this without startio set means we are being asked to make a dirty
  * page ready for freeing it's buffers.  When called with startio set then
@@ -1125,7 +1257,7 @@ error:
         */
        if (err != -EAGAIN) {
                if (!unmapped)
-                       block_invalidatepage(page, 0);
+                       xfs_aops_discard_page(page);
                ClearPageUptodate(page);
        }
        return err;
@@ -1535,15 +1667,6 @@ xfs_vm_readpages(
        return mpage_readpages(mapping, pages, nr_pages, xfs_get_blocks);
 }
 
-STATIC void
-xfs_vm_invalidatepage(
-       struct page             *page,
-       unsigned long           offset)
-{
-       trace_xfs_invalidatepage(page->mapping->host, page, offset);
-       block_invalidatepage(page, offset);
-}
-
 const struct address_space_operations xfs_address_space_operations = {
        .readpage               = xfs_vm_readpage,
        .readpages              = xfs_vm_readpages,
index 87b8cbd23d4bc5a9e287dbfbea056d11994612cf..846b75aeb2abe2b260a8efda2a15a73a2b8c18ab 100644 (file)
@@ -29,6 +29,7 @@
 #include "xfs_vnodeops.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_inode.h"
+#include "xfs_inode_item.h"
 
 /*
  * Note that we only accept fileids which are long enough rather than allow
@@ -215,9 +216,28 @@ xfs_fs_get_parent(
        return d_obtain_alias(VFS_I(cip));
 }
 
+STATIC int
+xfs_fs_nfs_commit_metadata(
+       struct inode            *inode)
+{
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_mount        *mp = ip->i_mount;
+       int                     error = 0;
+
+       xfs_ilock(ip, XFS_ILOCK_SHARED);
+       if (xfs_ipincount(ip)) {
+               error = _xfs_log_force_lsn(mp, ip->i_itemp->ili_last_lsn,
+                               XFS_LOG_SYNC, NULL);
+       }
+       xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+       return error;
+}
+
 const struct export_operations xfs_export_operations = {
        .encode_fh              = xfs_fs_encode_fh,
        .fh_to_dentry           = xfs_fs_fh_to_dentry,
        .fh_to_parent           = xfs_fs_fh_to_parent,
        .get_parent             = xfs_fs_get_parent,
+       .commit_metadata        = xfs_fs_nfs_commit_metadata,
 };
index e4caeb28ce2edab5b77f7149adc288b0f51d23b6..42dd3bcfba6b89e6afe21c02b6bac98f9544d0cb 100644 (file)
@@ -16,6 +16,7 @@
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 #include "xfs.h"
+#include "xfs_fs.h"
 #include "xfs_bit.h"
 #include "xfs_log.h"
 #include "xfs_inum.h"
 #include "xfs_dir2_sf.h"
 #include "xfs_dinode.h"
 #include "xfs_inode.h"
+#include "xfs_inode_item.h"
+#include "xfs_bmap.h"
 #include "xfs_error.h"
 #include "xfs_rw.h"
 #include "xfs_vnodeops.h"
 #include "xfs_da_btree.h"
 #include "xfs_ioctl.h"
+#include "xfs_trace.h"
 
 #include <linux/dcache.h>
 
 static const struct vm_operations_struct xfs_file_vm_ops;
 
-STATIC ssize_t
-xfs_file_aio_read(
-       struct kiocb            *iocb,
-       const struct iovec      *iov,
-       unsigned long           nr_segs,
-       loff_t                  pos)
+/*
+ *     xfs_iozero
+ *
+ *     xfs_iozero clears the specified range of buffer supplied,
+ *     and marks all the affected blocks as valid and modified.  If
+ *     an affected block is not allocated, it will be allocated.  If
+ *     an affected block is not completely overwritten, and is not
+ *     valid before the operation, it will be read from disk before
+ *     being partially zeroed.
+ */
+STATIC int
+xfs_iozero(
+       struct xfs_inode        *ip,    /* inode                        */
+       loff_t                  pos,    /* offset in file               */
+       size_t                  count)  /* size of data to zero         */
 {
-       struct file             *file = iocb->ki_filp;
-       int                     ioflags = 0;
+       struct page             *page;
+       struct address_space    *mapping;
+       int                     status;
 
-       BUG_ON(iocb->ki_pos != pos);
-       if (unlikely(file->f_flags & O_DIRECT))
-               ioflags |= IO_ISDIRECT;
-       if (file->f_mode & FMODE_NOCMTIME)
-               ioflags |= IO_INVIS;
-       return xfs_read(XFS_I(file->f_path.dentry->d_inode), iocb, iov,
-                               nr_segs, &iocb->ki_pos, ioflags);
+       mapping = VFS_I(ip)->i_mapping;
+       do {
+               unsigned offset, bytes;
+               void *fsdata;
+
+               offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+               bytes = PAGE_CACHE_SIZE - offset;
+               if (bytes > count)
+                       bytes = count;
+
+               status = pagecache_write_begin(NULL, mapping, pos, bytes,
+                                       AOP_FLAG_UNINTERRUPTIBLE,
+                                       &page, &fsdata);
+               if (status)
+                       break;
+
+               zero_user(page, offset, bytes);
+
+               status = pagecache_write_end(NULL, mapping, pos, bytes, bytes,
+                                       page, fsdata);
+               WARN_ON(status <= 0); /* can't return less than zero! */
+               pos += bytes;
+               count -= bytes;
+               status = 0;
+       } while (count);
+
+       return (-status);
+}
+
+STATIC int
+xfs_file_fsync(
+       struct file             *file,
+       struct dentry           *dentry,
+       int                     datasync)
+{
+       struct xfs_inode        *ip = XFS_I(dentry->d_inode);
+       struct xfs_trans        *tp;
+       int                     error = 0;
+       int                     log_flushed = 0;
+
+       xfs_itrace_entry(ip);
+
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+               return -XFS_ERROR(EIO);
+
+       xfs_iflags_clear(ip, XFS_ITRUNCATED);
+
+       /*
+        * We always need to make sure that the required inode state is safe on
+        * disk.  The inode might be clean but we still might need to force the
+        * log because of committed transactions that haven't hit the disk yet.
+        * Likewise, there could be unflushed non-transactional changes to the
+        * inode core that have to go to disk and this requires us to issue
+        * a synchronous transaction to capture these changes correctly.
+        *
+        * This code relies on the assumption that if the i_update_core field
+        * of the inode is clear and the inode is unpinned then it is clean
+        * and no action is required.
+        */
+       xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+       /*
+        * First check if the VFS inode is marked dirty.  All the dirtying
+        * of non-transactional updates no goes through mark_inode_dirty*,
+        * which allows us to distinguish beteeen pure timestamp updates
+        * and i_size updates which need to be caught for fdatasync.
+        * After that also theck for the dirty state in the XFS inode, which
+        * might gets cleared when the inode gets written out via the AIL
+        * or xfs_iflush_cluster.
+        */
+       if (((dentry->d_inode->i_state & I_DIRTY_DATASYNC) ||
+           ((dentry->d_inode->i_state & I_DIRTY_SYNC) && !datasync)) &&
+           ip->i_update_core) {
+               /*
+                * Kick off a transaction to log the inode core to get the
+                * updates.  The sync transaction will also force the log.
+                */
+               xfs_iunlock(ip, XFS_ILOCK_SHARED);
+               tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_FSYNC_TS);
+               error = xfs_trans_reserve(tp, 0,
+                               XFS_FSYNC_TS_LOG_RES(ip->i_mount), 0, 0, 0);
+               if (error) {
+                       xfs_trans_cancel(tp, 0);
+                       return -error;
+               }
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+               /*
+                * Note - it's possible that we might have pushed ourselves out
+                * of the way during trans_reserve which would flush the inode.
+                * But there's no guarantee that the inode buffer has actually
+                * gone out yet (it's delwri).  Plus the buffer could be pinned
+                * anyway if it's part of an inode in another recent
+                * transaction.  So we play it safe and fire off the
+                * transaction anyway.
+                */
+               xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+               xfs_trans_ihold(tp, ip);
+               xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+               xfs_trans_set_sync(tp);
+               error = _xfs_trans_commit(tp, 0, &log_flushed);
+
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       } else {
+               /*
+                * Timestamps/size haven't changed since last inode flush or
+                * inode transaction commit.  That means either nothing got
+                * written or a transaction committed which caught the updates.
+                * If the latter happened and the transaction hasn't hit the
+                * disk yet, the inode will be still be pinned.  If it is,
+                * force the log.
+                */
+               if (xfs_ipincount(ip)) {
+                       error = _xfs_log_force_lsn(ip->i_mount,
+                                       ip->i_itemp->ili_last_lsn,
+                                       XFS_LOG_SYNC, &log_flushed);
+               }
+               xfs_iunlock(ip, XFS_ILOCK_SHARED);
+       }
+
+       if (ip->i_mount->m_flags & XFS_MOUNT_BARRIER) {
+               /*
+                * If the log write didn't issue an ordered tag we need
+                * to flush the disk cache for the data device now.
+                */
+               if (!log_flushed)
+                       xfs_blkdev_issue_flush(ip->i_mount->m_ddev_targp);
+
+               /*
+                * If this inode is on the RT dev we need to flush that
+                * cache as well.
+                */
+               if (XFS_IS_REALTIME_INODE(ip))
+                       xfs_blkdev_issue_flush(ip->i_mount->m_rtdev_targp);
+       }
+
+       return -error;
 }
 
 STATIC ssize_t
-xfs_file_aio_write(
+xfs_file_aio_read(
        struct kiocb            *iocb,
-       const struct iovec      *iov,
+       const struct iovec      *iovp,
        unsigned long           nr_segs,
        loff_t                  pos)
 {
        struct file             *file = iocb->ki_filp;
+       struct inode            *inode = file->f_mapping->host;
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_mount        *mp = ip->i_mount;
+       size_t                  size = 0;
+       ssize_t                 ret = 0;
        int                     ioflags = 0;
+       xfs_fsize_t             n;
+       unsigned long           seg;
+
+       XFS_STATS_INC(xs_read_calls);
 
        BUG_ON(iocb->ki_pos != pos);
+
        if (unlikely(file->f_flags & O_DIRECT))
                ioflags |= IO_ISDIRECT;
        if (file->f_mode & FMODE_NOCMTIME)
                ioflags |= IO_INVIS;
-       return xfs_write(XFS_I(file->f_mapping->host), iocb, iov, nr_segs,
-                               &iocb->ki_pos, ioflags);
+
+       /* START copy & waste from filemap.c */
+       for (seg = 0; seg < nr_segs; seg++) {
+               const struct iovec *iv = &iovp[seg];
+
+               /*
+                * If any segment has a negative length, or the cumulative
+                * length ever wraps negative then return -EINVAL.
+                */
+               size += iv->iov_len;
+               if (unlikely((ssize_t)(size|iv->iov_len) < 0))
+                       return XFS_ERROR(-EINVAL);
+       }
+       /* END copy & waste from filemap.c */
+
+       if (unlikely(ioflags & IO_ISDIRECT)) {
+               xfs_buftarg_t   *target =
+                       XFS_IS_REALTIME_INODE(ip) ?
+                               mp->m_rtdev_targp : mp->m_ddev_targp;
+               if ((iocb->ki_pos & target->bt_smask) ||
+                   (size & target->bt_smask)) {
+                       if (iocb->ki_pos == ip->i_size)
+                               return 0;
+                       return -XFS_ERROR(EINVAL);
+               }
+       }
+
+       n = XFS_MAXIOFFSET(mp) - iocb->ki_pos;
+       if (n <= 0 || size == 0)
+               return 0;
+
+       if (n < size)
+               size = n;
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+       if (unlikely(ioflags & IO_ISDIRECT))
+               mutex_lock(&inode->i_mutex);
+       xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+       if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) {
+               int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags);
+               int iolock = XFS_IOLOCK_SHARED;
+
+               ret = -XFS_SEND_DATA(mp, DM_EVENT_READ, ip, iocb->ki_pos, size,
+                                       dmflags, &iolock);
+               if (ret) {
+                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+                       if (unlikely(ioflags & IO_ISDIRECT))
+                               mutex_unlock(&inode->i_mutex);
+                       return ret;
+               }
+       }
+
+       if (unlikely(ioflags & IO_ISDIRECT)) {
+               if (inode->i_mapping->nrpages) {
+                       ret = -xfs_flushinval_pages(ip,
+                                       (iocb->ki_pos & PAGE_CACHE_MASK),
+                                       -1, FI_REMAPF_LOCKED);
+               }
+               mutex_unlock(&inode->i_mutex);
+               if (ret) {
+                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+                       return ret;
+               }
+       }
+
+       trace_xfs_file_read(ip, size, iocb->ki_pos, ioflags);
+
+       ret = generic_file_aio_read(iocb, iovp, nr_segs, iocb->ki_pos);
+       if (ret > 0)
+               XFS_STATS_ADD(xs_read_bytes, ret);
+
+       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+       return ret;
 }
 
 STATIC ssize_t
@@ -87,16 +315,44 @@ xfs_file_splice_read(
        struct file             *infilp,
        loff_t                  *ppos,
        struct pipe_inode_info  *pipe,
-       size_t                  len,
+       size_t                  count,
        unsigned int            flags)
 {
+       struct xfs_inode        *ip = XFS_I(infilp->f_mapping->host);
+       struct xfs_mount        *mp = ip->i_mount;
        int                     ioflags = 0;
+       ssize_t                 ret;
+
+       XFS_STATS_INC(xs_read_calls);
 
        if (infilp->f_mode & FMODE_NOCMTIME)
                ioflags |= IO_INVIS;
 
-       return xfs_splice_read(XFS_I(infilp->f_path.dentry->d_inode),
-                                  infilp, ppos, pipe, len, flags, ioflags);
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+               return -EIO;
+
+       xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+       if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) {
+               int iolock = XFS_IOLOCK_SHARED;
+               int error;
+
+               error = XFS_SEND_DATA(mp, DM_EVENT_READ, ip, *ppos, count,
+                                       FILP_DELAY_FLAG(infilp), &iolock);
+               if (error) {
+                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+                       return -error;
+               }
+       }
+
+       trace_xfs_file_splice_read(ip, count, *ppos, ioflags);
+
+       ret = generic_file_splice_read(infilp, ppos, pipe, count, flags);
+       if (ret > 0)
+               XFS_STATS_ADD(xs_read_bytes, ret);
+
+       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+       return ret;
 }
 
 STATIC ssize_t
@@ -104,16 +360,538 @@ xfs_file_splice_write(
        struct pipe_inode_info  *pipe,
        struct file             *outfilp,
        loff_t                  *ppos,
-       size_t                  len,
+       size_t                  count,
        unsigned int            flags)
 {
+       struct inode            *inode = outfilp->f_mapping->host;
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fsize_t             isize, new_size;
        int                     ioflags = 0;
+       ssize_t                 ret;
+
+       XFS_STATS_INC(xs_write_calls);
 
        if (outfilp->f_mode & FMODE_NOCMTIME)
                ioflags |= IO_INVIS;
 
-       return xfs_splice_write(XFS_I(outfilp->f_path.dentry->d_inode),
-                                   pipe, outfilp, ppos, len, flags, ioflags);
+       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+               return -EIO;
+
+       xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+       if (DM_EVENT_ENABLED(ip, DM_EVENT_WRITE) && !(ioflags & IO_INVIS)) {
+               int iolock = XFS_IOLOCK_EXCL;
+               int error;
+
+               error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, ip, *ppos, count,
+                                       FILP_DELAY_FLAG(outfilp), &iolock);
+               if (error) {
+                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+                       return -error;
+               }
+       }
+
+       new_size = *ppos + count;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       if (new_size > ip->i_size)
+               ip->i_new_size = new_size;
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       trace_xfs_file_splice_write(ip, count, *ppos, ioflags);
+
+       ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags);
+       if (ret > 0)
+               XFS_STATS_ADD(xs_write_bytes, ret);
+
+       isize = i_size_read(inode);
+       if (unlikely(ret < 0 && ret != -EFAULT && *ppos > isize))
+               *ppos = isize;
+
+       if (*ppos > ip->i_size) {
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+               if (*ppos > ip->i_size)
+                       ip->i_size = *ppos;
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       }
+
+       if (ip->i_new_size) {
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+               ip->i_new_size = 0;
+               if (ip->i_d.di_size > ip->i_size)
+                       ip->i_d.di_size = ip->i_size;
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       }
+       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+       return ret;
+}
+
+/*
+ * This routine is called to handle zeroing any space in the last
+ * block of the file that is beyond the EOF.  We do this since the
+ * size is being increased without writing anything to that block
+ * and we don't want anyone to read the garbage on the disk.
+ */
+STATIC int                             /* error (positive) */
+xfs_zero_last_block(
+       xfs_inode_t     *ip,
+       xfs_fsize_t     offset,
+       xfs_fsize_t     isize)
+{
+       xfs_fileoff_t   last_fsb;
+       xfs_mount_t     *mp = ip->i_mount;
+       int             nimaps;
+       int             zero_offset;
+       int             zero_len;
+       int             error = 0;
+       xfs_bmbt_irec_t imap;
+
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+
+       zero_offset = XFS_B_FSB_OFFSET(mp, isize);
+       if (zero_offset == 0) {
+               /*
+                * There are no extra bytes in the last block on disk to
+                * zero, so return.
+                */
+               return 0;
+       }
+
+       last_fsb = XFS_B_TO_FSBT(mp, isize);
+       nimaps = 1;
+       error = xfs_bmapi(NULL, ip, last_fsb, 1, 0, NULL, 0, &imap,
+                         &nimaps, NULL, NULL);
+       if (error) {
+               return error;
+       }
+       ASSERT(nimaps > 0);
+       /*
+        * If the block underlying isize is just a hole, then there
+        * is nothing to zero.
+        */
+       if (imap.br_startblock == HOLESTARTBLOCK) {
+               return 0;
+       }
+       /*
+        * Zero the part of the last block beyond the EOF, and write it
+        * out sync.  We need to drop the ilock while we do this so we
+        * don't deadlock when the buffer cache calls back to us.
+        */
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       zero_len = mp->m_sb.sb_blocksize - zero_offset;
+       if (isize + zero_len > offset)
+               zero_len = offset - isize;
+       error = xfs_iozero(ip, isize, zero_len);
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       ASSERT(error >= 0);
+       return error;
+}
+
+/*
+ * Zero any on disk space between the current EOF and the new,
+ * larger EOF.  This handles the normal case of zeroing the remainder
+ * of the last block in the file and the unusual case of zeroing blocks
+ * out beyond the size of the file.  This second case only happens
+ * with fixed size extents and when the system crashes before the inode
+ * size was updated but after blocks were allocated.  If fill is set,
+ * then any holes in the range are filled and zeroed.  If not, the holes
+ * are left alone as holes.
+ */
+
+int                                    /* error (positive) */
+xfs_zero_eof(
+       xfs_inode_t     *ip,
+       xfs_off_t       offset,         /* starting I/O offset */
+       xfs_fsize_t     isize)          /* current inode size */
+{
+       xfs_mount_t     *mp = ip->i_mount;
+       xfs_fileoff_t   start_zero_fsb;
+       xfs_fileoff_t   end_zero_fsb;
+       xfs_fileoff_t   zero_count_fsb;
+       xfs_fileoff_t   last_fsb;
+       xfs_fileoff_t   zero_off;
+       xfs_fsize_t     zero_len;
+       int             nimaps;
+       int             error = 0;
+       xfs_bmbt_irec_t imap;
+
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
+       ASSERT(offset > isize);
+
+       /*
+        * First handle zeroing the block on which isize resides.
+        * We only zero a part of that block so it is handled specially.
+        */
+       error = xfs_zero_last_block(ip, offset, isize);
+       if (error) {
+               ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
+               return error;
+       }
+
+       /*
+        * Calculate the range between the new size and the old
+        * where blocks needing to be zeroed may exist.  To get the
+        * block where the last byte in the file currently resides,
+        * we need to subtract one from the size and truncate back
+        * to a block boundary.  We subtract 1 in case the size is
+        * exactly on a block boundary.
+        */
+       last_fsb = isize ? XFS_B_TO_FSBT(mp, isize - 1) : (xfs_fileoff_t)-1;
+       start_zero_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)isize);
+       end_zero_fsb = XFS_B_TO_FSBT(mp, offset - 1);
+       ASSERT((xfs_sfiloff_t)last_fsb < (xfs_sfiloff_t)start_zero_fsb);
+       if (last_fsb == end_zero_fsb) {
+               /*
+                * The size was only incremented on its last block.
+                * We took care of that above, so just return.
+                */
+               return 0;
+       }
+
+       ASSERT(start_zero_fsb <= end_zero_fsb);
+       while (start_zero_fsb <= end_zero_fsb) {
+               nimaps = 1;
+               zero_count_fsb = end_zero_fsb - start_zero_fsb + 1;
+               error = xfs_bmapi(NULL, ip, start_zero_fsb, zero_count_fsb,
+                                 0, NULL, 0, &imap, &nimaps, NULL, NULL);
+               if (error) {
+                       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
+                       return error;
+               }
+               ASSERT(nimaps > 0);
+
+               if (imap.br_state == XFS_EXT_UNWRITTEN ||
+                   imap.br_startblock == HOLESTARTBLOCK) {
+                       /*
+                        * This loop handles initializing pages that were
+                        * partially initialized by the code below this
+                        * loop. It basically zeroes the part of the page
+                        * that sits on a hole and sets the page as P_HOLE
+                        * and calls remapf if it is a mapped file.
+                        */
+                       start_zero_fsb = imap.br_startoff + imap.br_blockcount;
+                       ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
+                       continue;
+               }
+
+               /*
+                * There are blocks we need to zero.
+                * Drop the inode lock while we're doing the I/O.
+                * We'll still have the iolock to protect us.
+                */
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+               zero_off = XFS_FSB_TO_B(mp, start_zero_fsb);
+               zero_len = XFS_FSB_TO_B(mp, imap.br_blockcount);
+
+               if ((zero_off + zero_len) > offset)
+                       zero_len = offset - zero_off;
+
+               error = xfs_iozero(ip, zero_off, zero_len);
+               if (error) {
+                       goto out_lock;
+               }
+
+               start_zero_fsb = imap.br_startoff + imap.br_blockcount;
+               ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
+
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+       }
+
+       return 0;
+
+out_lock:
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       ASSERT(error >= 0);
+       return error;
+}
+
+STATIC ssize_t
+xfs_file_aio_write(
+       struct kiocb            *iocb,
+       const struct iovec      *iovp,
+       unsigned long           nr_segs,
+       loff_t                  pos)
+{
+       struct file             *file = iocb->ki_filp;
+       struct address_space    *mapping = file->f_mapping;
+       struct inode            *inode = mapping->host;
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_mount        *mp = ip->i_mount;
+       ssize_t                 ret = 0, error = 0;
+       int                     ioflags = 0;
+       xfs_fsize_t             isize, new_size;
+       int                     iolock;
+       int                     eventsent = 0;
+       size_t                  ocount = 0, count;
+       int                     need_i_mutex;
+
+       XFS_STATS_INC(xs_write_calls);
+
+       BUG_ON(iocb->ki_pos != pos);
+
+       if (unlikely(file->f_flags & O_DIRECT))
+               ioflags |= IO_ISDIRECT;
+       if (file->f_mode & FMODE_NOCMTIME)
+               ioflags |= IO_INVIS;
+
+       error = generic_segment_checks(iovp, &nr_segs, &ocount, VERIFY_READ);
+       if (error)
+               return error;
+
+       count = ocount;
+       if (count == 0)
+               return 0;
+
+       xfs_wait_for_freeze(mp, SB_FREEZE_WRITE);
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+relock:
+       if (ioflags & IO_ISDIRECT) {
+               iolock = XFS_IOLOCK_SHARED;
+               need_i_mutex = 0;
+       } else {
+               iolock = XFS_IOLOCK_EXCL;
+               need_i_mutex = 1;
+               mutex_lock(&inode->i_mutex);
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL|iolock);
+
+start:
+       error = -generic_write_checks(file, &pos, &count,
+                                       S_ISBLK(inode->i_mode));
+       if (error) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL|iolock);
+               goto out_unlock_mutex;
+       }
+
+       if ((DM_EVENT_ENABLED(ip, DM_EVENT_WRITE) &&
+           !(ioflags & IO_INVIS) && !eventsent)) {
+               int             dmflags = FILP_DELAY_FLAG(file);
+
+               if (need_i_mutex)
+                       dmflags |= DM_FLAGS_IMUX;
+
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               error = XFS_SEND_DATA(ip->i_mount, DM_EVENT_WRITE, ip,
+                                     pos, count, dmflags, &iolock);
+               if (error) {
+                       goto out_unlock_internal;
+               }
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+               eventsent = 1;
+
+               /*
+                * The iolock was dropped and reacquired in XFS_SEND_DATA
+                * so we have to recheck the size when appending.
+                * We will only "goto start;" once, since having sent the
+                * event prevents another call to XFS_SEND_DATA, which is
+                * what allows the size to change in the first place.
+                */
+               if ((file->f_flags & O_APPEND) && pos != ip->i_size)
+                       goto start;
+       }
+
+       if (ioflags & IO_ISDIRECT) {
+               xfs_buftarg_t   *target =
+                       XFS_IS_REALTIME_INODE(ip) ?
+                               mp->m_rtdev_targp : mp->m_ddev_targp;
+
+               if ((pos & target->bt_smask) || (count & target->bt_smask)) {
+                       xfs_iunlock(ip, XFS_ILOCK_EXCL|iolock);
+                       return XFS_ERROR(-EINVAL);
+               }
+
+               if (!need_i_mutex && (mapping->nrpages || pos > ip->i_size)) {
+                       xfs_iunlock(ip, XFS_ILOCK_EXCL|iolock);
+                       iolock = XFS_IOLOCK_EXCL;
+                       need_i_mutex = 1;
+                       mutex_lock(&inode->i_mutex);
+                       xfs_ilock(ip, XFS_ILOCK_EXCL|iolock);
+                       goto start;
+               }
+       }
+
+       new_size = pos + count;
+       if (new_size > ip->i_size)
+               ip->i_new_size = new_size;
+
+       if (likely(!(ioflags & IO_INVIS)))
+               file_update_time(file);
+
+       /*
+        * If the offset is beyond the size of the file, we have a couple
+        * of things to do. First, if there is already space allocated
+        * we need to either create holes or zero the disk or ...
+        *
+        * If there is a page where the previous size lands, we need
+        * to zero it out up to the new size.
+        */
+
+       if (pos > ip->i_size) {
+               error = xfs_zero_eof(ip, pos, ip->i_size);
+               if (error) {
+                       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+                       goto out_unlock_internal;
+               }
+       }
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       /*
+        * If we're writing the file then make sure to clear the
+        * setuid and setgid bits if the process is not being run
+        * by root.  This keeps people from modifying setuid and
+        * setgid binaries.
+        */
+       error = -file_remove_suid(file);
+       if (unlikely(error))
+               goto out_unlock_internal;
+
+       /* We can write back this queue in page reclaim */
+       current->backing_dev_info = mapping->backing_dev_info;
+
+       if ((ioflags & IO_ISDIRECT)) {
+               if (mapping->nrpages) {
+                       WARN_ON(need_i_mutex == 0);
+                       error = xfs_flushinval_pages(ip,
+                                       (pos & PAGE_CACHE_MASK),
+                                       -1, FI_REMAPF_LOCKED);
+                       if (error)
+                               goto out_unlock_internal;
+               }
+
+               if (need_i_mutex) {
+                       /* demote the lock now the cached pages are gone */
+                       xfs_ilock_demote(ip, XFS_IOLOCK_EXCL);
+                       mutex_unlock(&inode->i_mutex);
+
+                       iolock = XFS_IOLOCK_SHARED;
+                       need_i_mutex = 0;
+               }
+
+               trace_xfs_file_direct_write(ip, count, iocb->ki_pos, ioflags);
+               ret = generic_file_direct_write(iocb, iovp,
+                               &nr_segs, pos, &iocb->ki_pos, count, ocount);
+
+               /*
+                * direct-io write to a hole: fall through to buffered I/O
+                * for completing the rest of the request.
+                */
+               if (ret >= 0 && ret != count) {
+                       XFS_STATS_ADD(xs_write_bytes, ret);
+
+                       pos += ret;
+                       count -= ret;
+
+                       ioflags &= ~IO_ISDIRECT;
+                       xfs_iunlock(ip, iolock);
+                       goto relock;
+               }
+       } else {
+               int enospc = 0;
+               ssize_t ret2 = 0;
+
+write_retry:
+               trace_xfs_file_buffered_write(ip, count, iocb->ki_pos, ioflags);
+               ret2 = generic_file_buffered_write(iocb, iovp, nr_segs,
+                               pos, &iocb->ki_pos, count, ret);
+               /*
+                * if we just got an ENOSPC, flush the inode now we
+                * aren't holding any page locks and retry *once*
+                */
+               if (ret2 == -ENOSPC && !enospc) {
+                       error = xfs_flush_pages(ip, 0, -1, 0, FI_NONE);
+                       if (error)
+                               goto out_unlock_internal;
+                       enospc = 1;
+                       goto write_retry;
+               }
+               ret = ret2;
+       }
+
+       current->backing_dev_info = NULL;
+
+       isize = i_size_read(inode);
+       if (unlikely(ret < 0 && ret != -EFAULT && iocb->ki_pos > isize))
+               iocb->ki_pos = isize;
+
+       if (iocb->ki_pos > ip->i_size) {
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+               if (iocb->ki_pos > ip->i_size)
+                       ip->i_size = iocb->ki_pos;
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       }
+
+       if (ret == -ENOSPC &&
+           DM_EVENT_ENABLED(ip, DM_EVENT_NOSPACE) && !(ioflags & IO_INVIS)) {
+               xfs_iunlock(ip, iolock);
+               if (need_i_mutex)
+                       mutex_unlock(&inode->i_mutex);
+               error = XFS_SEND_NAMESP(ip->i_mount, DM_EVENT_NOSPACE, ip,
+                               DM_RIGHT_NULL, ip, DM_RIGHT_NULL, NULL, NULL,
+                               0, 0, 0); /* Delay flag intentionally  unused */
+               if (need_i_mutex)
+                       mutex_lock(&inode->i_mutex);
+               xfs_ilock(ip, iolock);
+               if (error)
+                       goto out_unlock_internal;
+               goto start;
+       }
+
+       error = -ret;
+       if (ret <= 0)
+               goto out_unlock_internal;
+
+       XFS_STATS_ADD(xs_write_bytes, ret);
+
+       /* Handle various SYNC-type writes */
+       if ((file->f_flags & O_DSYNC) || IS_SYNC(inode)) {
+               loff_t end = pos + ret - 1;
+               int error2;
+
+               xfs_iunlock(ip, iolock);
+               if (need_i_mutex)
+                       mutex_unlock(&inode->i_mutex);
+
+               error2 = filemap_write_and_wait_range(mapping, pos, end);
+               if (!error)
+                       error = error2;
+               if (need_i_mutex)
+                       mutex_lock(&inode->i_mutex);
+               xfs_ilock(ip, iolock);
+
+               error2 = -xfs_file_fsync(file, file->f_path.dentry,
+                                        (file->f_flags & __O_SYNC) ? 0 : 1);
+               if (!error)
+                       error = error2;
+       }
+
+ out_unlock_internal:
+       if (ip->i_new_size) {
+               xfs_ilock(ip, XFS_ILOCK_EXCL);
+               ip->i_new_size = 0;
+               /*
+                * If this was a direct or synchronous I/O that failed (such
+                * as ENOSPC) then part of the I/O may have been written to
+                * disk before the error occured.  In this case the on-disk
+                * file size may have been adjusted beyond the in-memory file
+                * size and now needs to be truncated back.
+                */
+               if (ip->i_d.di_size > ip->i_size)
+                       ip->i_d.di_size = ip->i_size;
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       }
+       xfs_iunlock(ip, iolock);
+ out_unlock_mutex:
+       if (need_i_mutex)
+               mutex_unlock(&inode->i_mutex);
+       return -error;
 }
 
 STATIC int
@@ -160,28 +938,6 @@ xfs_file_release(
        return -xfs_release(XFS_I(inode));
 }
 
-/*
- * We ignore the datasync flag here because a datasync is effectively
- * identical to an fsync. That is, datasync implies that we need to write
- * only the metadata needed to be able to access the data that is written
- * if we crash after the call completes. Hence if we are writing beyond
- * EOF we have to log the inode size change as well, which makes it a
- * full fsync. If we don't write beyond EOF, the inode core will be
- * clean in memory and so we don't need to log the inode, just like
- * fsync.
- */
-STATIC int
-xfs_file_fsync(
-       struct file             *file,
-       struct dentry           *dentry,
-       int                     datasync)
-{
-       struct xfs_inode        *ip = XFS_I(dentry->d_inode);
-
-       xfs_iflags_clear(ip, XFS_ITRUNCATED);
-       return -xfs_fsync(ip);
-}
-
 STATIC int
 xfs_file_readdir(
        struct file     *filp,
@@ -203,9 +959,9 @@ xfs_file_readdir(
         *
         * Try to give it an estimate that's good enough, maybe at some
         * point we can change the ->readdir prototype to include the
-        * buffer size.
+        * buffer size.  For now we use the current glibc buffer size.
         */
-       bufsize = (size_t)min_t(loff_t, PAGE_SIZE, ip->i_d.di_size);
+       bufsize = (size_t)min_t(loff_t, 32768, ip->i_d.di_size);
 
        error = xfs_readdir(ip, dirent, bufsize,
                                (xfs_off_t *)&filp->f_pos, filldir);
index e8566bbf0f00ffeb2154bee1b351cdcf402c4985..61a99608731e641fde3aac01c0c38c2fbcb51cea 100644 (file)
@@ -91,6 +91,16 @@ xfs_mark_inode_dirty_sync(
                mark_inode_dirty_sync(inode);
 }
 
+void
+xfs_mark_inode_dirty(
+       xfs_inode_t     *ip)
+{
+       struct inode    *inode = VFS_I(ip);
+
+       if (!(inode->i_state & (I_WILL_FREE|I_FREEING|I_CLEAR)))
+               mark_inode_dirty(inode);
+}
+
 /*
  * Change the requested timestamp in the given inode.
  * We don't lock across timestamp updates, and we don't log them but
index 5af0c81ca1ae1d61b978d70dc437843db35cb75e..facfb323a706a914de84de68b504762c82f05d6c 100644 (file)
@@ -88,7 +88,6 @@
 #include <xfs_super.h>
 #include <xfs_globals.h>
 #include <xfs_fs_subr.h>
-#include <xfs_lrw.h>
 #include <xfs_buf.h>
 
 /*
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
deleted file mode 100644 (file)
index eac6f80..0000000
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-#include "xfs.h"
-#include "xfs_fs.h"
-#include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_sb.h"
-#include "xfs_ag.h"
-#include "xfs_dir2.h"
-#include "xfs_alloc.h"
-#include "xfs_dmapi.h"
-#include "xfs_quota.h"
-#include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dir2_sf.h"
-#include "xfs_attr_sf.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
-#include "xfs_bmap.h"
-#include "xfs_btree.h"
-#include "xfs_ialloc.h"
-#include "xfs_rtalloc.h"
-#include "xfs_error.h"
-#include "xfs_itable.h"
-#include "xfs_rw.h"
-#include "xfs_attr.h"
-#include "xfs_inode_item.h"
-#include "xfs_buf_item.h"
-#include "xfs_utils.h"
-#include "xfs_iomap.h"
-#include "xfs_vnodeops.h"
-#include "xfs_trace.h"
-
-#include <linux/capability.h>
-#include <linux/writeback.h>
-
-
-/*
- *     xfs_iozero
- *
- *     xfs_iozero clears the specified range of buffer supplied,
- *     and marks all the affected blocks as valid and modified.  If
- *     an affected block is not allocated, it will be allocated.  If
- *     an affected block is not completely overwritten, and is not
- *     valid before the operation, it will be read from disk before
- *     being partially zeroed.
- */
-STATIC int
-xfs_iozero(
-       struct xfs_inode        *ip,    /* inode                        */
-       loff_t                  pos,    /* offset in file               */
-       size_t                  count)  /* size of data to zero         */
-{
-       struct page             *page;
-       struct address_space    *mapping;
-       int                     status;
-
-       mapping = VFS_I(ip)->i_mapping;
-       do {
-               unsigned offset, bytes;
-               void *fsdata;
-
-               offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
-               bytes = PAGE_CACHE_SIZE - offset;
-               if (bytes > count)
-                       bytes = count;
-
-               status = pagecache_write_begin(NULL, mapping, pos, bytes,
-                                       AOP_FLAG_UNINTERRUPTIBLE,
-                                       &page, &fsdata);
-               if (status)
-                       break;
-
-               zero_user(page, offset, bytes);
-
-               status = pagecache_write_end(NULL, mapping, pos, bytes, bytes,
-                                       page, fsdata);
-               WARN_ON(status <= 0); /* can't return less than zero! */
-               pos += bytes;
-               count -= bytes;
-               status = 0;
-       } while (count);
-
-       return (-status);
-}
-
-ssize_t                        /* bytes read, or (-)  error */
-xfs_read(
-       xfs_inode_t             *ip,
-       struct kiocb            *iocb,
-       const struct iovec      *iovp,
-       unsigned int            segs,
-       loff_t                  *offset,
-       int                     ioflags)
-{
-       struct file             *file = iocb->ki_filp;
-       struct inode            *inode = file->f_mapping->host;
-       xfs_mount_t             *mp = ip->i_mount;
-       size_t                  size = 0;
-       ssize_t                 ret = 0;
-       xfs_fsize_t             n;
-       unsigned long           seg;
-
-
-       XFS_STATS_INC(xs_read_calls);
-
-       /* START copy & waste from filemap.c */
-       for (seg = 0; seg < segs; seg++) {
-               const struct iovec *iv = &iovp[seg];
-
-               /*
-                * If any segment has a negative length, or the cumulative
-                * length ever wraps negative then return -EINVAL.
-                */
-               size += iv->iov_len;
-               if (unlikely((ssize_t)(size|iv->iov_len) < 0))
-                       return XFS_ERROR(-EINVAL);
-       }
-       /* END copy & waste from filemap.c */
-
-       if (unlikely(ioflags & IO_ISDIRECT)) {
-               xfs_buftarg_t   *target =
-                       XFS_IS_REALTIME_INODE(ip) ?
-                               mp->m_rtdev_targp : mp->m_ddev_targp;
-               if ((*offset & target->bt_smask) ||
-                   (size & target->bt_smask)) {
-                       if (*offset == ip->i_size) {
-                               return (0);
-                       }
-                       return -XFS_ERROR(EINVAL);
-               }
-       }
-
-       n = XFS_MAXIOFFSET(mp) - *offset;
-       if ((n <= 0) || (size == 0))
-               return 0;
-
-       if (n < size)
-               size = n;
-
-       if (XFS_FORCED_SHUTDOWN(mp))
-               return -EIO;
-
-       if (unlikely(ioflags & IO_ISDIRECT))
-               mutex_lock(&inode->i_mutex);
-       xfs_ilock(ip, XFS_IOLOCK_SHARED);
-
-       if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) {
-               int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags);
-               int iolock = XFS_IOLOCK_SHARED;
-
-               ret = -XFS_SEND_DATA(mp, DM_EVENT_READ, ip, *offset, size,
-                                       dmflags, &iolock);
-               if (ret) {
-                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-                       if (unlikely(ioflags & IO_ISDIRECT))
-                               mutex_unlock(&inode->i_mutex);
-                       return ret;
-               }
-       }
-
-       if (unlikely(ioflags & IO_ISDIRECT)) {
-               if (inode->i_mapping->nrpages)
-                       ret = -xfs_flushinval_pages(ip, (*offset & PAGE_CACHE_MASK),
-                                                   -1, FI_REMAPF_LOCKED);
-               mutex_unlock(&inode->i_mutex);
-               if (ret) {
-                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-                       return ret;
-               }
-       }
-
-       trace_xfs_file_read(ip, size, *offset, ioflags);
-
-       iocb->ki_pos = *offset;
-       ret = generic_file_aio_read(iocb, iovp, segs, *offset);
-       if (ret > 0)
-               XFS_STATS_ADD(xs_read_bytes, ret);
-
-       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-       return ret;
-}
-
-ssize_t
-xfs_splice_read(
-       xfs_inode_t             *ip,
-       struct file             *infilp,
-       loff_t                  *ppos,
-       struct pipe_inode_info  *pipe,
-       size_t                  count,
-       int                     flags,
-       int                     ioflags)
-{
-       xfs_mount_t             *mp = ip->i_mount;
-       ssize_t                 ret;
-
-       XFS_STATS_INC(xs_read_calls);
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-               return -EIO;
-
-       xfs_ilock(ip, XFS_IOLOCK_SHARED);
-
-       if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) {
-               int iolock = XFS_IOLOCK_SHARED;
-               int error;
-
-               error = XFS_SEND_DATA(mp, DM_EVENT_READ, ip, *ppos, count,
-                                       FILP_DELAY_FLAG(infilp), &iolock);
-               if (error) {
-                       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-                       return -error;
-               }
-       }
-
-       trace_xfs_file_splice_read(ip, count, *ppos, ioflags);
-
-       ret = generic_file_splice_read(infilp, ppos, pipe, count, flags);
-       if (ret > 0)
-               XFS_STATS_ADD(xs_read_bytes, ret);
-
-       xfs_iunlock(ip, XFS_IOLOCK_SHARED);
-       return ret;
-}
-
-ssize_t
-xfs_splice_write(
-       xfs_inode_t             *ip,
-       struct pipe_inode_info  *pipe,
-       struct file             *outfilp,
-       loff_t                  *ppos,
-       size_t                  count,
-       int                     flags,
-       int                     ioflags)
-{
-       xfs_mount_t             *mp = ip->i_mount;
-       ssize_t                 ret;
-       struct inode            *inode = outfilp->f_mapping->host;
-       xfs_fsize_t             isize, new_size;
-
-       XFS_STATS_INC(xs_write_calls);
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-               return -EIO;
-
-       xfs_ilock(ip, XFS_IOLOCK_EXCL);
-
-       if (DM_EVENT_ENABLED(ip, DM_EVENT_WRITE) && !(ioflags & IO_INVIS)) {
-               int iolock = XFS_IOLOCK_EXCL;
-               int error;
-
-               error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, ip, *ppos, count,
-                                       FILP_DELAY_FLAG(outfilp), &iolock);
-               if (error) {
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-                       return -error;
-               }
-       }
-
-       new_size = *ppos + count;
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       if (new_size > ip->i_size)
-               ip->i_new_size = new_size;
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-
-       trace_xfs_file_splice_write(ip, count, *ppos, ioflags);
-
-       ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags);
-       if (ret > 0)
-               XFS_STATS_ADD(xs_write_bytes, ret);
-
-       isize = i_size_read(inode);
-       if (unlikely(ret < 0 && ret != -EFAULT && *ppos > isize))
-               *ppos = isize;
-
-       if (*ppos > ip->i_size) {
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               if (*ppos > ip->i_size)
-                       ip->i_size = *ppos;
-               xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       }
-
-       if (ip->i_new_size) {
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               ip->i_new_size = 0;
-               if (ip->i_d.di_size > ip->i_size)
-                       ip->i_d.di_size = ip->i_size;
-               xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       }
-       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
-       return ret;
-}
-
-/*
- * This routine is called to handle zeroing any space in the last
- * block of the file that is beyond the EOF.  We do this since the
- * size is being increased without writing anything to that block
- * and we don't want anyone to read the garbage on the disk.
- */
-STATIC int                             /* error (positive) */
-xfs_zero_last_block(
-       xfs_inode_t     *ip,
-       xfs_fsize_t     offset,
-       xfs_fsize_t     isize)
-{
-       xfs_fileoff_t   last_fsb;
-       xfs_mount_t     *mp = ip->i_mount;
-       int             nimaps;
-       int             zero_offset;
-       int             zero_len;
-       int             error = 0;
-       xfs_bmbt_irec_t imap;
-
-       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-
-       zero_offset = XFS_B_FSB_OFFSET(mp, isize);
-       if (zero_offset == 0) {
-               /*
-                * There are no extra bytes in the last block on disk to
-                * zero, so return.
-                */
-               return 0;
-       }
-
-       last_fsb = XFS_B_TO_FSBT(mp, isize);
-       nimaps = 1;
-       error = xfs_bmapi(NULL, ip, last_fsb, 1, 0, NULL, 0, &imap,
-                         &nimaps, NULL, NULL);
-       if (error) {
-               return error;
-       }
-       ASSERT(nimaps > 0);
-       /*
-        * If the block underlying isize is just a hole, then there
-        * is nothing to zero.
-        */
-       if (imap.br_startblock == HOLESTARTBLOCK) {
-               return 0;
-       }
-       /*
-        * Zero the part of the last block beyond the EOF, and write it
-        * out sync.  We need to drop the ilock while we do this so we
-        * don't deadlock when the buffer cache calls back to us.
-        */
-       xfs_iunlock(ip, XFS_ILOCK_EXCL);
-
-       zero_len = mp->m_sb.sb_blocksize - zero_offset;
-       if (isize + zero_len > offset)
-               zero_len = offset - isize;
-       error = xfs_iozero(ip, isize, zero_len);
-
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       ASSERT(error >= 0);
-       return error;
-}
-
-/*
- * Zero any on disk space between the current EOF and the new,
- * larger EOF.  This handles the normal case of zeroing the remainder
- * of the last block in the file and the unusual case of zeroing blocks
- * out beyond the size of the file.  This second case only happens
- * with fixed size extents and when the system crashes before the inode
- * size was updated but after blocks were allocated.  If fill is set,
- * then any holes in the range are filled and zeroed.  If not, the holes
- * are left alone as holes.
- */
-
-int                                    /* error (positive) */
-xfs_zero_eof(
-       xfs_inode_t     *ip,
-       xfs_off_t       offset,         /* starting I/O offset */
-       xfs_fsize_t     isize)          /* current inode size */
-{
-       xfs_mount_t     *mp = ip->i_mount;
-       xfs_fileoff_t   start_zero_fsb;
-       xfs_fileoff_t   end_zero_fsb;
-       xfs_fileoff_t   zero_count_fsb;
-       xfs_fileoff_t   last_fsb;
-       xfs_fileoff_t   zero_off;
-       xfs_fsize_t     zero_len;
-       int             nimaps;
-       int             error = 0;
-       xfs_bmbt_irec_t imap;
-
-       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
-       ASSERT(offset > isize);
-
-       /*
-        * First handle zeroing the block on which isize resides.
-        * We only zero a part of that block so it is handled specially.
-        */
-       error = xfs_zero_last_block(ip, offset, isize);
-       if (error) {
-               ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
-               return error;
-       }
-
-       /*
-        * Calculate the range between the new size and the old
-        * where blocks needing to be zeroed may exist.  To get the
-        * block where the last byte in the file currently resides,
-        * we need to subtract one from the size and truncate back
-        * to a block boundary.  We subtract 1 in case the size is
-        * exactly on a block boundary.
-        */
-       last_fsb = isize ? XFS_B_TO_FSBT(mp, isize - 1) : (xfs_fileoff_t)-1;
-       start_zero_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)isize);
-       end_zero_fsb = XFS_B_TO_FSBT(mp, offset - 1);
-       ASSERT((xfs_sfiloff_t)last_fsb < (xfs_sfiloff_t)start_zero_fsb);
-       if (last_fsb == end_zero_fsb) {
-               /*
-                * The size was only incremented on its last block.
-                * We took care of that above, so just return.
-                */
-               return 0;
-       }
-
-       ASSERT(start_zero_fsb <= end_zero_fsb);
-       while (start_zero_fsb <= end_zero_fsb) {
-               nimaps = 1;
-               zero_count_fsb = end_zero_fsb - start_zero_fsb + 1;
-               error = xfs_bmapi(NULL, ip, start_zero_fsb, zero_count_fsb,
-                                 0, NULL, 0, &imap, &nimaps, NULL, NULL);
-               if (error) {
-                       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
-                       return error;
-               }
-               ASSERT(nimaps > 0);
-
-               if (imap.br_state == XFS_EXT_UNWRITTEN ||
-                   imap.br_startblock == HOLESTARTBLOCK) {
-                       /*
-                        * This loop handles initializing pages that were
-                        * partially initialized by the code below this
-                        * loop. It basically zeroes the part of the page
-                        * that sits on a hole and sets the page as P_HOLE
-                        * and calls remapf if it is a mapped file.
-                        */
-                       start_zero_fsb = imap.br_startoff + imap.br_blockcount;
-                       ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
-                       continue;
-               }
-
-               /*
-                * There are blocks we need to zero.
-                * Drop the inode lock while we're doing the I/O.
-                * We'll still have the iolock to protect us.
-                */
-               xfs_iunlock(ip, XFS_ILOCK_EXCL);
-
-               zero_off = XFS_FSB_TO_B(mp, start_zero_fsb);
-               zero_len = XFS_FSB_TO_B(mp, imap.br_blockcount);
-
-               if ((zero_off + zero_len) > offset)
-                       zero_len = offset - zero_off;
-
-               error = xfs_iozero(ip, zero_off, zero_len);
-               if (error) {
-                       goto out_lock;
-               }
-
-               start_zero_fsb = imap.br_startoff + imap.br_blockcount;
-               ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
-
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-       }
-
-       return 0;
-
-out_lock:
-       xfs_ilock(ip, XFS_ILOCK_EXCL);
-       ASSERT(error >= 0);
-       return error;
-}
-
-ssize_t                                /* bytes written, or (-) error */
-xfs_write(
-       struct xfs_inode        *xip,
-       struct kiocb            *iocb,
-       const struct iovec      *iovp,
-       unsigned int            nsegs,
-       loff_t                  *offset,
-       int                     ioflags)
-{
-       struct file             *file = iocb->ki_filp;
-       struct address_space    *mapping = file->f_mapping;
-       struct inode            *inode = mapping->host;
-       unsigned long           segs = nsegs;
-       xfs_mount_t             *mp;
-       ssize_t                 ret = 0, error = 0;
-       xfs_fsize_t             isize, new_size;
-       int                     iolock;
-       int                     eventsent = 0;
-       size_t                  ocount = 0, count;
-       loff_t                  pos;
-       int                     need_i_mutex;
-
-       XFS_STATS_INC(xs_write_calls);
-
-       error = generic_segment_checks(iovp, &segs, &ocount, VERIFY_READ);
-       if (error)
-               return error;
-
-       count = ocount;
-       pos = *offset;
-
-       if (count == 0)
-               return 0;
-
-       mp = xip->i_mount;
-
-       xfs_wait_for_freeze(mp, SB_FREEZE_WRITE);
-
-       if (XFS_FORCED_SHUTDOWN(mp))
-               return -EIO;
-
-relock:
-       if (ioflags & IO_ISDIRECT) {
-               iolock = XFS_IOLOCK_SHARED;
-               need_i_mutex = 0;
-       } else {
-               iolock = XFS_IOLOCK_EXCL;
-               need_i_mutex = 1;
-               mutex_lock(&inode->i_mutex);
-       }
-
-       xfs_ilock(xip, XFS_ILOCK_EXCL|iolock);
-
-start:
-       error = -generic_write_checks(file, &pos, &count,
-                                       S_ISBLK(inode->i_mode));
-       if (error) {
-               xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
-               goto out_unlock_mutex;
-       }
-
-       if ((DM_EVENT_ENABLED(xip, DM_EVENT_WRITE) &&
-           !(ioflags & IO_INVIS) && !eventsent)) {
-               int             dmflags = FILP_DELAY_FLAG(file);
-
-               if (need_i_mutex)
-                       dmflags |= DM_FLAGS_IMUX;
-
-               xfs_iunlock(xip, XFS_ILOCK_EXCL);
-               error = XFS_SEND_DATA(xip->i_mount, DM_EVENT_WRITE, xip,
-                                     pos, count, dmflags, &iolock);
-               if (error) {
-                       goto out_unlock_internal;
-               }
-               xfs_ilock(xip, XFS_ILOCK_EXCL);
-               eventsent = 1;
-
-               /*
-                * The iolock was dropped and reacquired in XFS_SEND_DATA
-                * so we have to recheck the size when appending.
-                * We will only "goto start;" once, since having sent the
-                * event prevents another call to XFS_SEND_DATA, which is
-                * what allows the size to change in the first place.
-                */
-               if ((file->f_flags & O_APPEND) && pos != xip->i_size)
-                       goto start;
-       }
-
-       if (ioflags & IO_ISDIRECT) {
-               xfs_buftarg_t   *target =
-                       XFS_IS_REALTIME_INODE(xip) ?
-                               mp->m_rtdev_targp : mp->m_ddev_targp;
-
-               if ((pos & target->bt_smask) || (count & target->bt_smask)) {
-                       xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
-                       return XFS_ERROR(-EINVAL);
-               }
-
-               if (!need_i_mutex && (mapping->nrpages || pos > xip->i_size)) {
-                       xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
-                       iolock = XFS_IOLOCK_EXCL;
-                       need_i_mutex = 1;
-                       mutex_lock(&inode->i_mutex);
-                       xfs_ilock(xip, XFS_ILOCK_EXCL|iolock);
-                       goto start;
-               }
-       }
-
-       new_size = pos + count;
-       if (new_size > xip->i_size)
-               xip->i_new_size = new_size;
-
-       if (likely(!(ioflags & IO_INVIS)))
-               file_update_time(file);
-
-       /*
-        * If the offset is beyond the size of the file, we have a couple
-        * of things to do. First, if there is already space allocated
-        * we need to either create holes or zero the disk or ...
-        *
-        * If there is a page where the previous size lands, we need
-        * to zero it out up to the new size.
-        */
-
-       if (pos > xip->i_size) {
-               error = xfs_zero_eof(xip, pos, xip->i_size);
-               if (error) {
-                       xfs_iunlock(xip, XFS_ILOCK_EXCL);
-                       goto out_unlock_internal;
-               }
-       }
-       xfs_iunlock(xip, XFS_ILOCK_EXCL);
-
-       /*
-        * If we're writing the file then make sure to clear the
-        * setuid and setgid bits if the process is not being run
-        * by root.  This keeps people from modifying setuid and
-        * setgid binaries.
-        */
-       error = -file_remove_suid(file);
-       if (unlikely(error))
-               goto out_unlock_internal;
-
-       /* We can write back this queue in page reclaim */
-       current->backing_dev_info = mapping->backing_dev_info;
-
-       if ((ioflags & IO_ISDIRECT)) {
-               if (mapping->nrpages) {
-                       WARN_ON(need_i_mutex == 0);
-                       error = xfs_flushinval_pages(xip,
-                                       (pos & PAGE_CACHE_MASK),
-                                       -1, FI_REMAPF_LOCKED);
-                       if (error)
-                               goto out_unlock_internal;
-               }
-
-               if (need_i_mutex) {
-                       /* demote the lock now the cached pages are gone */
-                       xfs_ilock_demote(xip, XFS_IOLOCK_EXCL);
-                       mutex_unlock(&inode->i_mutex);
-
-                       iolock = XFS_IOLOCK_SHARED;
-                       need_i_mutex = 0;
-               }
-
-               trace_xfs_file_direct_write(xip, count, *offset, ioflags);
-               ret = generic_file_direct_write(iocb, iovp,
-                               &segs, pos, offset, count, ocount);
-
-               /*
-                * direct-io write to a hole: fall through to buffered I/O
-                * for completing the rest of the request.
-                */
-               if (ret >= 0 && ret != count) {
-                       XFS_STATS_ADD(xs_write_bytes, ret);
-
-                       pos += ret;
-                       count -= ret;
-
-                       ioflags &= ~IO_ISDIRECT;
-                       xfs_iunlock(xip, iolock);
-                       goto relock;
-               }
-       } else {
-               int enospc = 0;
-               ssize_t ret2 = 0;
-
-write_retry:
-               trace_xfs_file_buffered_write(xip, count, *offset, ioflags);
-               ret2 = generic_file_buffered_write(iocb, iovp, segs,
-                               pos, offset, count, ret);
-               /*
-                * if we just got an ENOSPC, flush the inode now we
-                * aren't holding any page locks and retry *once*
-                */
-               if (ret2 == -ENOSPC && !enospc) {
-                       error = xfs_flush_pages(xip, 0, -1, 0, FI_NONE);
-                       if (error)
-                               goto out_unlock_internal;
-                       enospc = 1;
-                       goto write_retry;
-               }
-               ret = ret2;
-       }
-
-       current->backing_dev_info = NULL;
-
-       isize = i_size_read(inode);
-       if (unlikely(ret < 0 && ret != -EFAULT && *offset > isize))
-               *offset = isize;
-
-       if (*offset > xip->i_size) {
-               xfs_ilock(xip, XFS_ILOCK_EXCL);
-               if (*offset > xip->i_size)
-                       xip->i_size = *offset;
-               xfs_iunlock(xip, XFS_ILOCK_EXCL);
-       }
-
-       if (ret == -ENOSPC &&
-           DM_EVENT_ENABLED(xip, DM_EVENT_NOSPACE) && !(ioflags & IO_INVIS)) {
-               xfs_iunlock(xip, iolock);
-               if (need_i_mutex)
-                       mutex_unlock(&inode->i_mutex);
-               error = XFS_SEND_NAMESP(xip->i_mount, DM_EVENT_NOSPACE, xip,
-                               DM_RIGHT_NULL, xip, DM_RIGHT_NULL, NULL, NULL,
-                               0, 0, 0); /* Delay flag intentionally  unused */
-               if (need_i_mutex)
-                       mutex_lock(&inode->i_mutex);
-               xfs_ilock(xip, iolock);
-               if (error)
-                       goto out_unlock_internal;
-               goto start;
-       }
-
-       error = -ret;
-       if (ret <= 0)
-               goto out_unlock_internal;
-
-       XFS_STATS_ADD(xs_write_bytes, ret);
-
-       /* Handle various SYNC-type writes */
-       if ((file->f_flags & O_DSYNC) || IS_SYNC(inode)) {
-               loff_t end = pos + ret - 1;
-               int error2;
-
-               xfs_iunlock(xip, iolock);
-               if (need_i_mutex)
-                       mutex_unlock(&inode->i_mutex);
-
-               error2 = filemap_write_and_wait_range(mapping, pos, end);
-               if (!error)
-                       error = error2;
-               if (need_i_mutex)
-                       mutex_lock(&inode->i_mutex);
-               xfs_ilock(xip, iolock);
-
-               error2 = xfs_fsync(xip);
-               if (!error)
-                       error = error2;
-       }
-
- out_unlock_internal:
-       if (xip->i_new_size) {
-               xfs_ilock(xip, XFS_ILOCK_EXCL);
-               xip->i_new_size = 0;
-               /*
-                * If this was a direct or synchronous I/O that failed (such
-                * as ENOSPC) then part of the I/O may have been written to
-                * disk before the error occured.  In this case the on-disk
-                * file size may have been adjusted beyond the in-memory file
-                * size and now needs to be truncated back.
-                */
-               if (xip->i_d.di_size > xip->i_size)
-                       xip->i_d.di_size = xip->i_size;
-               xfs_iunlock(xip, XFS_ILOCK_EXCL);
-       }
-       xfs_iunlock(xip, iolock);
- out_unlock_mutex:
-       if (need_i_mutex)
-               mutex_unlock(&inode->i_mutex);
-       return -error;
-}
-
-/*
- * If the underlying (data/log/rt) device is readonly, there are some
- * operations that cannot proceed.
- */
-int
-xfs_dev_is_read_only(
-       xfs_mount_t             *mp,
-       char                    *message)
-{
-       if (xfs_readonly_buftarg(mp->m_ddev_targp) ||
-           xfs_readonly_buftarg(mp->m_logdev_targp) ||
-           (mp->m_rtdev_targp && xfs_readonly_buftarg(mp->m_rtdev_targp))) {
-               cmn_err(CE_NOTE,
-                       "XFS: %s required on read-only device.", message);
-               cmn_err(CE_NOTE,
-                       "XFS: write access unavailable, cannot proceed.");
-               return EROFS;
-       }
-       return 0;
-}
diff --git a/fs/xfs/linux-2.6/xfs_lrw.h b/fs/xfs/linux-2.6/xfs_lrw.h
deleted file mode 100644 (file)
index 342ae8c..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would 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 the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-#ifndef __XFS_LRW_H__
-#define __XFS_LRW_H__
-
-struct xfs_mount;
-struct xfs_inode;
-struct xfs_buf;
-
-extern int xfs_dev_is_read_only(struct xfs_mount *, char *);
-
-extern int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
-
-#endif /* __XFS_LRW_H__ */
index 3d4a0c84d634f947f36eb3bad04128cbc603f17d..1947514ce1ad05722ec4ebea43219fe64f68037f 100644 (file)
@@ -43,20 +43,6 @@ xfs_quota_type(int type)
        }
 }
 
-STATIC int
-xfs_fs_quota_sync(
-       struct super_block      *sb,
-       int                     type)
-{
-       struct xfs_mount        *mp = XFS_M(sb);
-
-       if (sb->s_flags & MS_RDONLY)
-               return -EROFS;
-       if (!XFS_IS_QUOTA_RUNNING(mp))
-               return -ENOSYS;
-       return -xfs_sync_data(mp, 0);
-}
-
 STATIC int
 xfs_fs_get_xstate(
        struct super_block      *sb,
@@ -82,8 +68,6 @@ xfs_fs_set_xstate(
                return -EROFS;
        if (op != Q_XQUOTARM && !XFS_IS_QUOTA_RUNNING(mp))
                return -ENOSYS;
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
 
        if (uflags & XFS_QUOTA_UDQ_ACCT)
                flags |= XFS_UQUOTA_ACCT;
@@ -144,14 +128,11 @@ xfs_fs_set_xquota(
                return -ENOSYS;
        if (!XFS_IS_QUOTA_ON(mp))
                return -ESRCH;
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
 
        return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
 }
 
 const struct quotactl_ops xfs_quotactl_operations = {
-       .quota_sync             = xfs_fs_quota_sync,
        .get_xstate             = xfs_fs_get_xstate,
        .set_xstate             = xfs_fs_set_xstate,
        .get_xquota             = xfs_fs_get_xquota,
index 25ea2408118fb0fba760ef955922eed742116ee2..71345a370d9f7ad0f7d6561bf860ed5b08593fbd 100644 (file)
@@ -1063,7 +1063,7 @@ xfs_log_inode(
 STATIC int
 xfs_fs_write_inode(
        struct inode            *inode,
-       int                     sync)
+       struct writeback_control *wbc)
 {
        struct xfs_inode        *ip = XFS_I(inode);
        struct xfs_mount        *mp = ip->i_mount;
@@ -1074,11 +1074,7 @@ xfs_fs_write_inode(
        if (XFS_FORCED_SHUTDOWN(mp))
                return XFS_ERROR(EIO);
 
-       if (sync) {
-               error = xfs_wait_on_pages(ip, 0, -1);
-               if (error)
-                       goto out;
-
+       if (wbc->sync_mode == WB_SYNC_ALL) {
                /*
                 * Make sure the inode has hit stable storage.  By using the
                 * log and the fsync transactions we reduce the IOs we have
index a9f6d20aff4152e23029b2e3fa96a8836aa117b0..05cd85317f6f53b6e0fe77bf0426c890f927b07a 100644 (file)
@@ -607,7 +607,8 @@ xfssyncd(
        set_freezable();
        timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10);
        for (;;) {
-               timeleft = schedule_timeout_interruptible(timeleft);
+               if (list_empty(&mp->m_sync_list))
+                       timeleft = schedule_timeout_interruptible(timeleft);
                /* swsusp */
                try_to_freeze();
                if (kthread_should_stop() && list_empty(&mp->m_sync_list))
@@ -627,8 +628,7 @@ xfssyncd(
                        list_add_tail(&mp->m_sync_work.w_list,
                                        &mp->m_sync_list);
                }
-               list_for_each_entry_safe(work, n, &mp->m_sync_list, w_list)
-                       list_move(&work->w_list, &tmp);
+               list_splice_init(&mp->m_sync_list, &tmp);
                spin_unlock(&mp->m_sync_lock);
 
                list_for_each_entry_safe(work, n, &tmp, w_list) {
@@ -688,12 +688,12 @@ xfs_inode_set_reclaim_tag(
        struct xfs_perag *pag;
 
        pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
-       read_lock(&pag->pag_ici_lock);
+       write_lock(&pag->pag_ici_lock);
        spin_lock(&ip->i_flags_lock);
        __xfs_inode_set_reclaim_tag(pag, ip);
        __xfs_iflags_set(ip, XFS_IRECLAIMABLE);
        spin_unlock(&ip->i_flags_lock);
-       read_unlock(&pag->pag_ici_lock);
+       write_unlock(&pag->pag_ici_lock);
        xfs_perag_put(pag);
 }
 
index 856eb3c8d6054b5937cad5d4c327e737095a3da2..5a107601e9696889df87bd28a4147f3c90cbbf8f 100644 (file)
 #include "quota/xfs_dquot_item.h"
 #include "quota/xfs_dquot.h"
 
-/*
- * Format fsblock number into a static buffer & return it.
- */
-STATIC char *xfs_fmtfsblock(xfs_fsblock_t bno)
-{
-       static char rval[50];
-
-       if (bno == NULLFSBLOCK)
-               sprintf(rval, "NULLFSBLOCK");
-       else if (isnullstartblock(bno))
-               sprintf(rval, "NULLSTARTBLOCK(%lld)", startblockval(bno));
-       else
-               sprintf(rval, "%lld", (xfs_dfsbno_t)bno);
-       return rval;
-}
-
 /*
  * We include this last to have the helpers above available for the trace
  * event implementations.
index a4574dcf5065f9ac9f0b7a98059c37fb5d4b3ab9..fcaa62f0799e7e80135da20bb877d4ed98bc1892 100644 (file)
@@ -197,13 +197,13 @@ TRACE_EVENT(xfs_iext_insert,
                __entry->caller_ip = caller_ip;
        ),
        TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
-                 "offset %lld block %s count %lld flag %d caller %pf",
+                 "offset %lld block %lld count %lld flag %d caller %pf",
                  MAJOR(__entry->dev), MINOR(__entry->dev),
                  __entry->ino,
                  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
                  (long)__entry->idx,
                  __entry->startoff,
-                 xfs_fmtfsblock(__entry->startblock),
+                 (__int64_t)__entry->startblock,
                  __entry->blockcount,
                  __entry->state,
                  (char *)__entry->caller_ip)
@@ -241,13 +241,13 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
                __entry->caller_ip = caller_ip;
        ),
        TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
-                 "offset %lld block %s count %lld flag %d caller %pf",
+                 "offset %lld block %lld count %lld flag %d caller %pf",
                  MAJOR(__entry->dev), MINOR(__entry->dev),
                  __entry->ino,
                  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
                  (long)__entry->idx,
                  __entry->startoff,
-                 xfs_fmtfsblock(__entry->startblock),
+                 (__int64_t)__entry->startblock,
                  __entry->blockcount,
                  __entry->state,
                  (char *)__entry->caller_ip)
@@ -593,7 +593,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class,
        TP_ARGS(dqp),
        TP_STRUCT__entry(
                __field(dev_t, dev)
-               __field(__be32, id)
+               __field(u32, id)
                __field(unsigned, flags)
                __field(unsigned, nrefs)
                __field(unsigned long long, res_bcount)
@@ -606,7 +606,7 @@ DECLARE_EVENT_CLASS(xfs_dquot_class,
        ), \
        TP_fast_assign(
                __entry->dev = dqp->q_mount->m_super->s_dev;
-               __entry->id = dqp->q_core.d_id;
+               __entry->id = be32_to_cpu(dqp->q_core.d_id);
                __entry->flags = dqp->dq_flags;
                __entry->nrefs = dqp->q_nrefs;
                __entry->res_bcount = dqp->q_res_bcount;
@@ -622,10 +622,10 @@ DECLARE_EVENT_CLASS(xfs_dquot_class,
                        be64_to_cpu(dqp->q_core.d_ino_softlimit);
        ),
        TP_printk("dev %d:%d id 0x%x flags %s nrefs %u res_bc 0x%llx "
-                 "bcnt 0x%llx [hard 0x%llx | soft 0x%llx] "
-                 "icnt 0x%llx [hard 0x%llx | soft 0x%llx]",
+                 "bcnt 0x%llx bhardlimit 0x%llx bsoftlimit 0x%llx "
+                 "icnt 0x%llx ihardlimit 0x%llx isoftlimit 0x%llx]",
                  MAJOR(__entry->dev), MINOR(__entry->dev),
-                 be32_to_cpu(__entry->id),
+                 __entry->id,
                  __print_flags(__entry->flags, "|", XFS_DQ_FLAGS),
                  __entry->nrefs,
                  __entry->res_bcount,
@@ -881,7 +881,7 @@ TRACE_EVENT(name, \
        ), \
        TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " \
                  "offset 0x%llx count %zd flags %s " \
-                 "startoff 0x%llx startblock %s blockcount 0x%llx", \
+                 "startoff 0x%llx startblock %lld blockcount 0x%llx", \
                  MAJOR(__entry->dev), MINOR(__entry->dev), \
                  __entry->ino, \
                  __entry->size, \
@@ -890,7 +890,7 @@ TRACE_EVENT(name, \
                  __entry->count, \
                  __print_flags(__entry->flags, "|", BMAPI_FLAGS), \
                  __entry->startoff, \
-                 xfs_fmtfsblock(__entry->startblock), \
+                 (__int64_t)__entry->startblock, \
                  __entry->blockcount) \
 )
 DEFINE_IOMAP_EVENT(xfs_iomap_enter);
index 1869fb97381968046491d9349684ff12bc038cee..5c11e4d17010957be70982be6354efafcfe14540 100644 (file)
@@ -2549,6 +2549,121 @@ xfs_bmap_rtalloc(
        return 0;
 }
 
+STATIC int
+xfs_bmap_btalloc_nullfb(
+       struct xfs_bmalloca     *ap,
+       struct xfs_alloc_arg    *args,
+       xfs_extlen_t            *blen)
+{
+       struct xfs_mount        *mp = ap->ip->i_mount;
+       struct xfs_perag        *pag;
+       xfs_agnumber_t          ag, startag;
+       int                     notinit = 0;
+       int                     error;
+
+       if (ap->userdata && xfs_inode_is_filestream(ap->ip))
+               args->type = XFS_ALLOCTYPE_NEAR_BNO;
+       else
+               args->type = XFS_ALLOCTYPE_START_BNO;
+       args->total = ap->total;
+
+       /*
+        * Search for an allocation group with a single extent large enough
+        * for the request.  If one isn't found, then adjust the minimum
+        * allocation size to the largest space found.
+        */
+       startag = ag = XFS_FSB_TO_AGNO(mp, args->fsbno);
+       if (startag == NULLAGNUMBER)
+               startag = ag = 0;
+
+       pag = xfs_perag_get(mp, ag);
+       while (*blen < ap->alen) {
+               if (!pag->pagf_init) {
+                       error = xfs_alloc_pagf_init(mp, args->tp, ag,
+                                                   XFS_ALLOC_FLAG_TRYLOCK);
+                       if (error) {
+                               xfs_perag_put(pag);
+                               return error;
+                       }
+               }
+
+               /*
+                * See xfs_alloc_fix_freelist...
+                */
+               if (pag->pagf_init) {
+                       xfs_extlen_t    longest;
+                       longest = xfs_alloc_longest_free_extent(mp, pag);
+                       if (*blen < longest)
+                               *blen = longest;
+               } else
+                       notinit = 1;
+
+               if (xfs_inode_is_filestream(ap->ip)) {
+                       if (*blen >= ap->alen)
+                               break;
+
+                       if (ap->userdata) {
+                               /*
+                                * If startag is an invalid AG, we've
+                                * come here once before and
+                                * xfs_filestream_new_ag picked the
+                                * best currently available.
+                                *
+                                * Don't continue looping, since we
+                                * could loop forever.
+                                */
+                               if (startag == NULLAGNUMBER)
+                                       break;
+
+                               error = xfs_filestream_new_ag(ap, &ag);
+                               xfs_perag_put(pag);
+                               if (error)
+                                       return error;
+
+                               /* loop again to set 'blen'*/
+                               startag = NULLAGNUMBER;
+                               pag = xfs_perag_get(mp, ag);
+                               continue;
+                       }
+               }
+               if (++ag == mp->m_sb.sb_agcount)
+                       ag = 0;
+               if (ag == startag)
+                       break;
+               xfs_perag_put(pag);
+               pag = xfs_perag_get(mp, ag);
+       }
+       xfs_perag_put(pag);
+
+       /*
+        * Since the above loop did a BUF_TRYLOCK, it is
+        * possible that there is space for this request.
+        */
+       if (notinit || *blen < ap->minlen)
+               args->minlen = ap->minlen;
+       /*
+        * If the best seen length is less than the request
+        * length, use the best as the minimum.
+        */
+       else if (*blen < ap->alen)
+               args->minlen = *blen;
+       /*
+        * Otherwise we've seen an extent as big as alen,
+        * use that as the minimum.
+        */
+       else
+               args->minlen = ap->alen;
+
+       /*
+        * set the failure fallback case to look in the selected
+        * AG as the stream may have moved.
+        */
+       if (xfs_inode_is_filestream(ap->ip))
+               ap->rval = args->fsbno = XFS_AGB_TO_FSB(mp, ag, 0);
+
+       return 0;
+}
+
 STATIC int
 xfs_bmap_btalloc(
        xfs_bmalloca_t  *ap)            /* bmap alloc argument struct */
@@ -2556,16 +2671,13 @@ xfs_bmap_btalloc(
        xfs_mount_t     *mp;            /* mount point structure */
        xfs_alloctype_t atype = 0;      /* type for allocation routines */
        xfs_extlen_t    align;          /* minimum allocation alignment */
-       xfs_agnumber_t  ag;
        xfs_agnumber_t  fb_agno;        /* ag number of ap->firstblock */
-       xfs_agnumber_t  startag;
+       xfs_agnumber_t  ag;
        xfs_alloc_arg_t args;
        xfs_extlen_t    blen;
        xfs_extlen_t    nextminlen = 0;
-       xfs_perag_t     *pag;
        int             nullfb;         /* true if ap->firstblock isn't set */
        int             isaligned;
-       int             notinit;
        int             tryagain;
        int             error;
 
@@ -2612,103 +2724,9 @@ xfs_bmap_btalloc(
        args.firstblock = ap->firstblock;
        blen = 0;
        if (nullfb) {
-               if (ap->userdata && xfs_inode_is_filestream(ap->ip))
-                       args.type = XFS_ALLOCTYPE_NEAR_BNO;
-               else
-                       args.type = XFS_ALLOCTYPE_START_BNO;
-               args.total = ap->total;
-
-               /*
-                * Search for an allocation group with a single extent
-                * large enough for the request.
-                *
-                * If one isn't found, then adjust the minimum allocation
-                * size to the largest space found.
-                */
-               startag = ag = XFS_FSB_TO_AGNO(mp, args.fsbno);
-               if (startag == NULLAGNUMBER)
-                       startag = ag = 0;
-               notinit = 0;
-               pag = xfs_perag_get(mp, ag);
-               while (blen < ap->alen) {
-                       if (!pag->pagf_init &&
-                           (error = xfs_alloc_pagf_init(mp, args.tp,
-                                   ag, XFS_ALLOC_FLAG_TRYLOCK))) {
-                               xfs_perag_put(pag);
-                               return error;
-                       }
-                       /*
-                        * See xfs_alloc_fix_freelist...
-                        */
-                       if (pag->pagf_init) {
-                               xfs_extlen_t    longest;
-                               longest = xfs_alloc_longest_free_extent(mp, pag);
-                               if (blen < longest)
-                                       blen = longest;
-                       } else
-                               notinit = 1;
-
-                       if (xfs_inode_is_filestream(ap->ip)) {
-                               if (blen >= ap->alen)
-                                       break;
-
-                               if (ap->userdata) {
-                                       /*
-                                        * If startag is an invalid AG, we've
-                                        * come here once before and
-                                        * xfs_filestream_new_ag picked the
-                                        * best currently available.
-                                        *
-                                        * Don't continue looping, since we
-                                        * could loop forever.
-                                        */
-                                       if (startag == NULLAGNUMBER)
-                                               break;
-
-                                       error = xfs_filestream_new_ag(ap, &ag);
-                                       xfs_perag_put(pag);
-                                       if (error)
-                                               return error;
-
-                                       /* loop again to set 'blen'*/
-                                       startag = NULLAGNUMBER;
-                                       pag = xfs_perag_get(mp, ag);
-                                       continue;
-                               }
-                       }
-                       if (++ag == mp->m_sb.sb_agcount)
-                               ag = 0;
-                       if (ag == startag)
-                               break;
-                       xfs_perag_put(pag);
-                       pag = xfs_perag_get(mp, ag);
-               }
-               xfs_perag_put(pag);
-               /*
-                * Since the above loop did a BUF_TRYLOCK, it is
-                * possible that there is space for this request.
-                */
-               if (notinit || blen < ap->minlen)
-                       args.minlen = ap->minlen;
-               /*
-                * If the best seen length is less than the request
-                * length, use the best as the minimum.
-                */
-               else if (blen < ap->alen)
-                       args.minlen = blen;
-               /*
-                * Otherwise we've seen an extent as big as alen,
-                * use that as the minimum.
-                */
-               else
-                       args.minlen = ap->alen;
-
-               /*
-                * set the failure fallback case to look in the selected
-                * AG as the stream may have moved.
-                */
-               if (xfs_inode_is_filestream(ap->ip))
-                       ap->rval = args.fsbno = XFS_AGB_TO_FSB(mp, ag, 0);
+               error = xfs_bmap_btalloc_nullfb(ap, &args, &blen);
+               if (error)
+                       return error;
        } else if (ap->low) {
                if (xfs_inode_is_filestream(ap->ip))
                        args.type = XFS_ALLOCTYPE_FIRST_AG;
index f52ac276277e47a66b35ab78e39734a8108fcfbb..7cf7220e7d5fb30dfece1fe9aa4f2798f3e017fc 100644 (file)
@@ -292,7 +292,8 @@ typedef struct xfs_bstat {
        __s32           bs_extents;     /* number of extents            */
        __u32           bs_gen;         /* generation count             */
        __u16           bs_projid;      /* project id                   */
-       unsigned char   bs_pad[14];     /* pad space, unused            */
+       __u16           bs_forkoff;     /* inode fork offset in bytes   */
+       unsigned char   bs_pad[12];     /* pad space, unused            */
        __u32           bs_dmevmask;    /* DMIG event mask              */
        __u16           bs_dmstate;     /* DMIG state info              */
        __u16           bs_aextents;    /* attribute number of extents  */
index e281eb4a1c4978791b511acf94ac07cb86efca1c..6845db90818f2223cf9fdbc735ed8a423b9338bc 100644 (file)
@@ -190,13 +190,12 @@ xfs_iget_cache_hit(
                trace_xfs_iget_reclaim(ip);
 
                /*
-                * We need to set XFS_INEW atomically with clearing the
-                * reclaimable tag so that we do have an indicator of the
-                * inode still being initialized.
+                * We need to set XFS_IRECLAIM to prevent xfs_reclaim_inode
+                * from stomping over us while we recycle the inode.  We can't
+                * clear the radix tree reclaimable tag yet as it requires
+                * pag_ici_lock to be held exclusive.
                 */
-               ip->i_flags |= XFS_INEW;
-               ip->i_flags &= ~XFS_IRECLAIMABLE;
-               __xfs_inode_clear_reclaim_tag(mp, pag, ip);
+               ip->i_flags |= XFS_IRECLAIM;
 
                spin_unlock(&ip->i_flags_lock);
                read_unlock(&pag->pag_ici_lock);
@@ -216,7 +215,15 @@ xfs_iget_cache_hit(
                        trace_xfs_iget_reclaim(ip);
                        goto out_error;
                }
+
+               write_lock(&pag->pag_ici_lock);
+               spin_lock(&ip->i_flags_lock);
+               ip->i_flags &= ~(XFS_IRECLAIMABLE | XFS_IRECLAIM);
+               ip->i_flags |= XFS_INEW;
+               __xfs_inode_clear_reclaim_tag(mp, pag, ip);
                inode->i_state = I_NEW;
+               spin_unlock(&ip->i_flags_lock);
+               write_unlock(&pag->pag_ici_lock);
        } else {
                /* If the VFS inode is being torn down, pause and try again. */
                if (!igrab(inode)) {
index fa31360046d48a1c07d999da3157740bf4729999..0ffd56447045a5bc8c817a03a64135b435e7b9d5 100644 (file)
@@ -2439,75 +2439,31 @@ xfs_idestroy_fork(
 }
 
 /*
- * Increment the pin count of the given buffer.
- * This value is protected by ipinlock spinlock in the mount structure.
+ * This is called to unpin an inode.  The caller must have the inode locked
+ * in at least shared mode so that the buffer cannot be subsequently pinned
+ * once someone is waiting for it to be unpinned.
  */
-void
-xfs_ipin(
-       xfs_inode_t     *ip)
-{
-       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-
-       atomic_inc(&ip->i_pincount);
-}
-
-/*
- * Decrement the pin count of the given inode, and wake up
- * anyone in xfs_iwait_unpin() if the count goes to 0.  The
- * inode must have been previously pinned with a call to xfs_ipin().
- */
-void
-xfs_iunpin(
-       xfs_inode_t     *ip)
-{
-       ASSERT(atomic_read(&ip->i_pincount) > 0);
-
-       if (atomic_dec_and_test(&ip->i_pincount))
-               wake_up(&ip->i_ipin_wait);
-}
-
-/*
- * This is called to unpin an inode. It can be directed to wait or to return
- * immediately without waiting for the inode to be unpinned.  The caller must
- * have the inode locked in at least shared mode so that the buffer cannot be
- * subsequently pinned once someone is waiting for it to be unpinned.
- */
-STATIC void
-__xfs_iunpin_wait(
-       xfs_inode_t     *ip,
-       int             wait)
+static void
+xfs_iunpin_nowait(
+       struct xfs_inode        *ip)
 {
-       xfs_inode_log_item_t    *iip = ip->i_itemp;
-
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
-       if (atomic_read(&ip->i_pincount) == 0)
-               return;
 
        /* Give the log a push to start the unpinning I/O */
-       if (iip && iip->ili_last_lsn)
-               xfs_log_force_lsn(ip->i_mount, iip->ili_last_lsn, 0);
-       else
-               xfs_log_force(ip->i_mount, 0);
+       xfs_log_force_lsn(ip->i_mount, ip->i_itemp->ili_last_lsn, 0);
 
-       if (wait)
-               wait_event(ip->i_ipin_wait, (atomic_read(&ip->i_pincount) == 0));
 }
 
 void
 xfs_iunpin_wait(
-       xfs_inode_t     *ip)
+       struct xfs_inode        *ip)
 {
-       __xfs_iunpin_wait(ip, 1);
-}
-
-static inline void
-xfs_iunpin_nowait(
-       xfs_inode_t     *ip)
-{
-       __xfs_iunpin_wait(ip, 0);
+       if (xfs_ipincount(ip)) {
+               xfs_iunpin_nowait(ip);
+               wait_event(ip->i_ipin_wait, (xfs_ipincount(ip) == 0));
+       }
 }
 
-
 /*
  * xfs_iextents_copy()
  *
index 6c912b02759603ddab355edf6afe4c9dd0269796..9965e40a4615d8c09001ffe800c7e750ab8bece7 100644 (file)
@@ -471,8 +471,6 @@ int         xfs_itruncate_finish(struct xfs_trans **, xfs_inode_t *,
 int            xfs_iunlink(struct xfs_trans *, xfs_inode_t *);
 
 void           xfs_iext_realloc(xfs_inode_t *, int, int);
-void           xfs_ipin(xfs_inode_t *);
-void           xfs_iunpin(xfs_inode_t *);
 void           xfs_iunpin_wait(xfs_inode_t *);
 int            xfs_iflush(xfs_inode_t *, uint);
 void           xfs_ichgtime(xfs_inode_t *, int);
@@ -480,6 +478,7 @@ void                xfs_lock_inodes(xfs_inode_t **, int, uint);
 void           xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint);
 
 void           xfs_synchronize_times(xfs_inode_t *);
+void           xfs_mark_inode_dirty(xfs_inode_t *);
 void           xfs_mark_inode_dirty_sync(xfs_inode_t *);
 
 #define IHOLD(ip) \
index d4dc063111f8f612d55413292cc1b3f90ee386a0..7bfea8540159f171bc639b0f0e8695da0f4b0ef5 100644 (file)
@@ -535,23 +535,23 @@ xfs_inode_item_format(
 
 /*
  * This is called to pin the inode associated with the inode log
- * item in memory so it cannot be written out.  Do this by calling
- * xfs_ipin() to bump the pin count in the inode while holding the
- * inode pin lock.
+ * item in memory so it cannot be written out.
  */
 STATIC void
 xfs_inode_item_pin(
        xfs_inode_log_item_t    *iip)
 {
        ASSERT(xfs_isilocked(iip->ili_inode, XFS_ILOCK_EXCL));
-       xfs_ipin(iip->ili_inode);
+
+       atomic_inc(&iip->ili_inode->i_pincount);
 }
 
 
 /*
  * This is called to unpin the inode associated with the inode log
  * item which was previously pinned with a call to xfs_inode_item_pin().
- * Just call xfs_iunpin() on the inode to do this.
+ *
+ * Also wake up anyone in xfs_iunpin_wait() if the count goes to 0.
  */
 /* ARGSUSED */
 STATIC void
@@ -559,7 +559,11 @@ xfs_inode_item_unpin(
        xfs_inode_log_item_t    *iip,
        int                     stale)
 {
-       xfs_iunpin(iip->ili_inode);
+       struct xfs_inode        *ip = iip->ili_inode;
+
+       ASSERT(atomic_read(&ip->i_pincount) > 0);
+       if (atomic_dec_and_test(&ip->i_pincount))
+               wake_up(&ip->i_ipin_wait);
 }
 
 /* ARGSUSED */
@@ -568,7 +572,7 @@ xfs_inode_item_unpin_remove(
        xfs_inode_log_item_t    *iip,
        xfs_trans_t             *tp)
 {
-       xfs_iunpin(iip->ili_inode);
+       xfs_inode_item_unpin(iip, 0);
 }
 
 /*
index 3af02314c605b47bf32d07605800bfdcdea6b0ff..b1b801e4a28e41643d93b6c5438e01718c665bed 100644 (file)
@@ -106,6 +106,7 @@ xfs_bulkstat_one_iget(
        buf->bs_dmevmask = dic->di_dmevmask;
        buf->bs_dmstate = dic->di_dmstate;
        buf->bs_aextents = dic->di_anextents;
+       buf->bs_forkoff = XFS_IFORK_BOFF(ip);
 
        switch (dic->di_format) {
        case XFS_DINODE_FMT_DEV:
@@ -176,6 +177,7 @@ xfs_bulkstat_one_dinode(
        buf->bs_dmevmask = be32_to_cpu(dic->di_dmevmask);
        buf->bs_dmstate = be16_to_cpu(dic->di_dmstate);
        buf->bs_aextents = be16_to_cpu(dic->di_anextents);
+       buf->bs_forkoff = XFS_DFORK_BOFF(dic);
 
        switch (dic->di_format) {
        case XFS_DINODE_FMT_DEV:
index 4f16be4b6ee5907295fcba07a2aac778866b68c3..e8fba92d7cd9f96b77fc2dc19cef07066ddadc12 100644 (file)
@@ -60,7 +60,7 @@ STATIC int     xlog_space_left(xlog_t *log, int cycle, int bytes);
 STATIC int      xlog_sync(xlog_t *log, xlog_in_core_t *iclog);
 STATIC void     xlog_dealloc_log(xlog_t *log);
 STATIC int      xlog_write(xfs_mount_t *mp, xfs_log_iovec_t region[],
-                           int nentries, xfs_log_ticket_t tic,
+                           int nentries, struct xlog_ticket *tic,
                            xfs_lsn_t *start_lsn,
                            xlog_in_core_t **commit_iclog,
                            uint flags);
@@ -243,14 +243,14 @@ xlog_tic_add_region(xlog_ticket_t *tic, uint len, uint type)
  * out when the next write occurs.
  */
 xfs_lsn_t
-xfs_log_done(xfs_mount_t       *mp,
-            xfs_log_ticket_t   xtic,
-            void               **iclog,
-            uint               flags)
+xfs_log_done(
+       struct xfs_mount        *mp,
+       struct xlog_ticket      *ticket,
+       struct xlog_in_core     **iclog,
+       uint                    flags)
 {
-       xlog_t          *log    = mp->m_log;
-       xlog_ticket_t   *ticket = (xfs_log_ticket_t) xtic;
-       xfs_lsn_t       lsn     = 0;
+       struct log              *log = mp->m_log;
+       xfs_lsn_t               lsn = 0;
 
        if (XLOG_FORCED_SHUTDOWN(log) ||
            /*
@@ -258,8 +258,7 @@ xfs_log_done(xfs_mount_t    *mp,
             * If we get an error, just continue and give back the log ticket.
             */
            (((ticket->t_flags & XLOG_TIC_INITED) == 0) &&
-            (xlog_commit_record(mp, ticket,
-                                (xlog_in_core_t **)iclog, &lsn)))) {
+            (xlog_commit_record(mp, ticket, iclog, &lsn)))) {
                lsn = (xfs_lsn_t) -1;
                if (ticket->t_flags & XLOG_TIC_PERM_RESERV) {
                        flags |= XFS_LOG_REL_PERM_RESERV;
@@ -289,7 +288,7 @@ xfs_log_done(xfs_mount_t    *mp,
        }
 
        return lsn;
-}      /* xfs_log_done */
+}
 
 /*
  * Attaches a new iclog I/O completion callback routine during
@@ -298,11 +297,11 @@ xfs_log_done(xfs_mount_t  *mp,
  * executing the callback at an appropriate time.
  */
 int
-xfs_log_notify(xfs_mount_t       *mp,          /* mount of partition */
-              void               *iclog_hndl,  /* iclog to hang callback off */
-              xfs_log_callback_t *cb)
+xfs_log_notify(
+       struct xfs_mount        *mp,
+       struct xlog_in_core     *iclog,
+       xfs_log_callback_t      *cb)
 {
-       xlog_in_core_t    *iclog = (xlog_in_core_t *)iclog_hndl;
        int     abortflg;
 
        spin_lock(&iclog->ic_callback_lock);
@@ -316,16 +315,14 @@ xfs_log_notify(xfs_mount_t          *mp,          /* mount of partition */
        }
        spin_unlock(&iclog->ic_callback_lock);
        return abortflg;
-}      /* xfs_log_notify */
+}
 
 int
-xfs_log_release_iclog(xfs_mount_t *mp,
-                     void        *iclog_hndl)
+xfs_log_release_iclog(
+       struct xfs_mount        *mp,
+       struct xlog_in_core     *iclog)
 {
-       xlog_t *log = mp->m_log;
-       xlog_in_core_t    *iclog = (xlog_in_core_t *)iclog_hndl;
-
-       if (xlog_state_release_iclog(log, iclog)) {
+       if (xlog_state_release_iclog(mp->m_log, iclog)) {
                xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
                return EIO;
        }
@@ -344,17 +341,18 @@ xfs_log_release_iclog(xfs_mount_t *mp,
  * reservation, we prevent over allocation problems.
  */
 int
-xfs_log_reserve(xfs_mount_t     *mp,
-               int              unit_bytes,
-               int              cnt,
-               xfs_log_ticket_t *ticket,
-               __uint8_t        client,
-               uint             flags,
-               uint             t_type)
+xfs_log_reserve(
+       struct xfs_mount        *mp,
+       int                     unit_bytes,
+       int                     cnt,
+       struct xlog_ticket      **ticket,
+       __uint8_t               client,
+       uint                    flags,
+       uint                    t_type)
 {
-       xlog_t          *log = mp->m_log;
-       xlog_ticket_t   *internal_ticket;
-       int             retval = 0;
+       struct log              *log = mp->m_log;
+       struct xlog_ticket      *internal_ticket;
+       int                     retval = 0;
 
        ASSERT(client == XFS_TRANSACTION || client == XFS_LOG);
        ASSERT((flags & XFS_LOG_NOSLEEP) == 0);
@@ -367,7 +365,7 @@ xfs_log_reserve(xfs_mount_t  *mp,
 
        if (*ticket != NULL) {
                ASSERT(flags & XFS_LOG_PERM_RESERV);
-               internal_ticket = (xlog_ticket_t *)*ticket;
+               internal_ticket = *ticket;
 
                trace_xfs_log_reserve(log, internal_ticket);
 
@@ -519,7 +517,7 @@ xfs_log_unmount_write(xfs_mount_t *mp)
        xlog_in_core_t   *first_iclog;
 #endif
        xfs_log_iovec_t  reg[1];
-       xfs_log_ticket_t tic = NULL;
+       xlog_ticket_t   *tic = NULL;
        xfs_lsn_t        lsn;
        int              error;
 
@@ -656,24 +654,24 @@ xfs_log_unmount(xfs_mount_t *mp)
  * transaction occur with one call to xfs_log_write().
  */
 int
-xfs_log_write(xfs_mount_t *    mp,
-             xfs_log_iovec_t   reg[],
-             int               nentries,
-             xfs_log_ticket_t  tic,
-             xfs_lsn_t         *start_lsn)
+xfs_log_write(
+       struct xfs_mount        *mp,
+       struct xfs_log_iovec    reg[],
+       int                     nentries,
+       struct xlog_ticket      *tic,
+       xfs_lsn_t               *start_lsn)
 {
-       int     error;
-       xlog_t *log = mp->m_log;
+       struct log              *log = mp->m_log;
+       int                     error;
 
        if (XLOG_FORCED_SHUTDOWN(log))
                return XFS_ERROR(EIO);
 
-       if ((error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0))) {
+       error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0);
+       if (error)
                xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
-       }
        return error;
-}      /* xfs_log_write */
-
+}
 
 void
 xfs_log_move_tail(xfs_mount_t  *mp,
@@ -1642,16 +1640,16 @@ xlog_print_tic_res(xfs_mount_t *mp, xlog_ticket_t *ticket)
  *     bytes have been written out.
  */
 STATIC int
-xlog_write(xfs_mount_t *       mp,
-          xfs_log_iovec_t      reg[],
-          int                  nentries,
-          xfs_log_ticket_t     tic,
-          xfs_lsn_t            *start_lsn,
-          xlog_in_core_t       **commit_iclog,
-          uint                 flags)
+xlog_write(
+       struct xfs_mount        *mp,
+       struct xfs_log_iovec    reg[],
+       int                     nentries,
+       struct xlog_ticket      *ticket,
+       xfs_lsn_t               *start_lsn,
+       struct xlog_in_core     **commit_iclog,
+       uint                    flags)
 {
     xlog_t          *log = mp->m_log;
-    xlog_ticket_t    *ticket = (xlog_ticket_t *)tic;
     xlog_in_core_t   *iclog = NULL;  /* ptr to current in-core log */
     xlog_op_header_t *logop_head;    /* ptr to log operation header */
     __psint_t       ptr;            /* copy address into data region */
@@ -1765,7 +1763,7 @@ xlog_write(xfs_mount_t *  mp,
            default:
                xfs_fs_cmn_err(CE_WARN, mp,
                    "Bad XFS transaction clientid 0x%x in ticket 0x%p",
-                   logop_head->oh_clientid, tic);
+                   logop_head->oh_clientid, ticket);
                return XFS_ERROR(EIO);
            }
 
index 7074be9d13e99113d45f48c469ba7b266b13fe4a..97a24c7795a4cf11ef6c68e7e8f3ef212af62e98 100644 (file)
@@ -110,8 +110,6 @@ typedef struct xfs_log_iovec {
        uint            i_type;         /* type of region */
 } xfs_log_iovec_t;
 
-typedef void* xfs_log_ticket_t;
-
 /*
  * Structure used to pass callback function and the function's argument
  * to the log manager.
@@ -126,10 +124,12 @@ typedef struct xfs_log_callback {
 #ifdef __KERNEL__
 /* Log manager interfaces */
 struct xfs_mount;
+struct xlog_in_core;
 struct xlog_ticket;
+
 xfs_lsn_t xfs_log_done(struct xfs_mount *mp,
-                      xfs_log_ticket_t ticket,
-                      void             **iclog,
+                      struct xlog_ticket *ticket,
+                      struct xlog_in_core **iclog,
                       uint             flags);
 int      _xfs_log_force(struct xfs_mount *mp,
                         uint           flags,
@@ -151,21 +151,21 @@ int         xfs_log_mount_finish(struct xfs_mount *mp);
 void     xfs_log_move_tail(struct xfs_mount    *mp,
                            xfs_lsn_t           tail_lsn);
 int      xfs_log_notify(struct xfs_mount       *mp,
-                        void                   *iclog,
+                        struct xlog_in_core    *iclog,
                         xfs_log_callback_t     *callback_entry);
 int      xfs_log_release_iclog(struct xfs_mount *mp,
-                        void                    *iclog_hndl);
+                        struct xlog_in_core     *iclog);
 int      xfs_log_reserve(struct xfs_mount *mp,
                          int              length,
                          int              count,
-                         xfs_log_ticket_t *ticket,
+                         struct xlog_ticket **ticket,
                          __uint8_t        clientid,
                          uint             flags,
                          uint             t_type);
 int      xfs_log_write(struct xfs_mount *mp,
                        xfs_log_iovec_t  region[],
                        int              nentries,
-                       xfs_log_ticket_t ticket,
+                       struct xlog_ticket *ticket,
                        xfs_lsn_t        *start_lsn);
 int      xfs_log_unmount_write(struct xfs_mount *mp);
 void      xfs_log_unmount(struct xfs_mount *mp);
index 6afaaeb2950abb5c2f15586865789547dfaee6b3..e79b56b4bca6634daaf0b4b1cc47fa689ff5048d 100644 (file)
@@ -1097,13 +1097,15 @@ xfs_default_resblks(xfs_mount_t *mp)
        __uint64_t resblks;
 
        /*
-        * We default to 5% or 1024 fsbs of space reserved, whichever is smaller.
-        * This may drive us straight to ENOSPC on mount, but that implies
-        * we were already there on the last unmount. Warn if this occurs.
+        * We default to 5% or 8192 fsbs of space reserved, whichever is
+        * smaller.  This is intended to cover concurrent allocation
+        * transactions when we initially hit enospc. These each require a 4
+        * block reservation. Hence by default we cover roughly 2000 concurrent
+        * allocation reservations.
         */
        resblks = mp->m_sb.sb_dblocks;
        do_div(resblks, 20);
-       resblks = min_t(__uint64_t, resblks, 1024);
+       resblks = min_t(__uint64_t, resblks, 8192);
        return resblks;
 }
 
@@ -1417,6 +1419,9 @@ xfs_mountfs(
         * when at ENOSPC. This is needed for operations like create with
         * attr, unwritten extent conversion at ENOSPC, etc. Data allocations
         * are not allowed to use this reserved space.
+        *
+        * This may drive us straight to ENOSPC on mount, but that implies
+        * we were already there on the last unmount. Warn if this occurs.
         */
        if (!(mp->m_flags & XFS_MOUNT_RDONLY)) {
                resblks = xfs_default_resblks(mp);
@@ -1725,26 +1730,30 @@ xfs_mod_incore_sb_unlocked(
                                lcounter += rem;
                        }
                } else {                                /* Taking blocks away */
-
                        lcounter += delta;
+                       if (lcounter >= 0) {
+                               mp->m_sb.sb_fdblocks = lcounter +
+                                                       XFS_ALLOC_SET_ASIDE(mp);
+                               return 0;
+                       }
 
-               /*
-                * If were out of blocks, use any available reserved blocks if
-                * were allowed to.
-                */
+                       /*
+                        * We are out of blocks, use any available reserved
+                        * blocks if were allowed to.
+                        */
+                       if (!rsvd)
+                               return XFS_ERROR(ENOSPC);
 
-                       if (lcounter < 0) {
-                               if (rsvd) {
-                                       lcounter = (long long)mp->m_resblks_avail + delta;
-                                       if (lcounter < 0) {
-                                               return XFS_ERROR(ENOSPC);
-                                       }
-                                       mp->m_resblks_avail = lcounter;
-                                       return 0;
-                               } else {        /* not reserved */
-                                       return XFS_ERROR(ENOSPC);
-                               }
+                       lcounter = (long long)mp->m_resblks_avail + delta;
+                       if (lcounter >= 0) {
+                               mp->m_resblks_avail = lcounter;
+                               return 0;
                        }
+                       printk_once(KERN_WARNING
+                               "Filesystem \"%s\": reserve blocks depleted! "
+                               "Consider increasing reserve pool size.",
+                               mp->m_fsname);
+                       return XFS_ERROR(ENOSPC);
                }
 
                mp->m_sb.sb_fdblocks = lcounter + XFS_ALLOC_SET_ASIDE(mp);
@@ -2052,6 +2061,26 @@ xfs_mount_log_sb(
        return error;
 }
 
+/*
+ * If the underlying (data/log/rt) device is readonly, there are some
+ * operations that cannot proceed.
+ */
+int
+xfs_dev_is_read_only(
+       struct xfs_mount        *mp,
+       char                    *message)
+{
+       if (xfs_readonly_buftarg(mp->m_ddev_targp) ||
+           xfs_readonly_buftarg(mp->m_logdev_targp) ||
+           (mp->m_rtdev_targp && xfs_readonly_buftarg(mp->m_rtdev_targp))) {
+               cmn_err(CE_NOTE,
+                       "XFS: %s required on read-only device.", message);
+               cmn_err(CE_NOTE,
+                       "XFS: write access unavailable, cannot proceed.");
+               return EROFS;
+       }
+       return 0;
+}
 
 #ifdef HAVE_PERCPU_SB
 /*
index 14dafd6082306f7bc0124a0752ac7378a5c08edf..4fa0bc7b983ebb0e934b4b877f63c6090fb73afa 100644 (file)
@@ -436,6 +436,8 @@ extern void xfs_freesb(xfs_mount_t *);
 extern int     xfs_fs_writable(xfs_mount_t *);
 extern int     xfs_sb_validate_fsb_count(struct xfs_sb *, __uint64_t);
 
+extern int     xfs_dev_is_read_only(struct xfs_mount *, char *);
+
 extern int     xfs_dmops_get(struct xfs_mount *);
 extern void    xfs_dmops_put(struct xfs_mount *);
 
index be942d4e3324335eef40a443a0c9e7132b2bc908..f73e358bae8d37ff05ae32310e97b66158172260 100644 (file)
@@ -796,7 +796,7 @@ _xfs_trans_commit(
        int                     sync;
 #define        XFS_TRANS_LOGVEC_COUNT  16
        xfs_log_iovec_t         log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
-       void                    *commit_iclog;
+       struct xlog_in_core     *commit_iclog;
        int                     shutdown;
 
        commit_lsn = -1;
index c93e3a10285705db91a93a3354eaabafbafe77ce..79c8bab9dfffa66dcfba268edf9c539c79cd7de0 100644 (file)
@@ -910,7 +910,7 @@ typedef struct xfs_trans {
        unsigned int            t_blk_res_used; /* # of resvd blocks used */
        unsigned int            t_rtx_res;      /* # of rt extents resvd */
        unsigned int            t_rtx_res_used; /* # of resvd rt extents used */
-       xfs_log_ticket_t        t_ticket;       /* log mgr ticket */
+       struct xlog_ticket      *t_ticket;      /* log mgr ticket */
        xfs_lsn_t               t_lsn;          /* log seq num of start of
                                                 * transaction. */
        xfs_lsn_t               t_commit_lsn;   /* log seq num of end of
index 5ffd544434eb8eaf4d79a439ad6400df2cbbf47a..fb586360d1c909da5d1e1231e099b5d8e64f0a67 100644 (file)
@@ -46,6 +46,65 @@ STATIC xfs_buf_t *xfs_trans_buf_item_match(xfs_trans_t *, xfs_buftarg_t *,
 STATIC xfs_buf_t *xfs_trans_buf_item_match_all(xfs_trans_t *, xfs_buftarg_t *,
                xfs_daddr_t, int);
 
+/*
+ * Add the locked buffer to the transaction.
+ *
+ * The buffer must be locked, and it cannot be associated with any
+ * transaction.
+ *
+ * If the buffer does not yet have a buf log item associated with it,
+ * then allocate one for it.  Then add the buf item to the transaction.
+ */
+STATIC void
+_xfs_trans_bjoin(
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
+       int                     reset_recur)
+{
+       struct xfs_buf_log_item *bip;
+
+       ASSERT(XFS_BUF_ISBUSY(bp));
+       ASSERT(XFS_BUF_FSPRIVATE2(bp, void *) == NULL);
+
+       /*
+        * The xfs_buf_log_item pointer is stored in b_fsprivate.  If
+        * it doesn't have one yet, then allocate one and initialize it.
+        * The checks to see if one is there are in xfs_buf_item_init().
+        */
+       xfs_buf_item_init(bp, tp->t_mountp);
+       bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+       ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+       ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+       ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
+       if (reset_recur)
+               bip->bli_recur = 0;
+
+       /*
+        * Take a reference for this transaction on the buf item.
+        */
+       atomic_inc(&bip->bli_refcount);
+
+       /*
+        * Get a log_item_desc to point at the new item.
+        */
+       (void) xfs_trans_add_item(tp, (xfs_log_item_t *)bip);
+
+       /*
+        * Initialize b_fsprivate2 so we can find it with incore_match()
+        * in xfs_trans_get_buf() and friends above.
+        */
+       XFS_BUF_SET_FSPRIVATE2(bp, tp);
+
+}
+
+void
+xfs_trans_bjoin(
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp)
+{
+       _xfs_trans_bjoin(tp, bp, 0);
+       trace_xfs_trans_bjoin(bp->b_fspriv);
+}
 
 /*
  * Get and lock the buffer for the caller if it is not already
@@ -132,40 +191,8 @@ xfs_trans_get_buf(xfs_trans_t      *tp,
 
        ASSERT(!XFS_BUF_GETERROR(bp));
 
-       /*
-        * The xfs_buf_log_item pointer is stored in b_fsprivate.  If
-        * it doesn't have one yet, then allocate one and initialize it.
-        * The checks to see if one is there are in xfs_buf_item_init().
-        */
-       xfs_buf_item_init(bp, tp->t_mountp);
-
-       /*
-        * Set the recursion count for the buffer within this transaction
-        * to 0.
-        */
-       bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
-       ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
-       ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
-       bip->bli_recur = 0;
-
-       /*
-        * Take a reference for this transaction on the buf item.
-        */
-       atomic_inc(&bip->bli_refcount);
-
-       /*
-        * Get a log_item_desc to point at the new item.
-        */
-       (void) xfs_trans_add_item(tp, (xfs_log_item_t*)bip);
-
-       /*
-        * Initialize b_fsprivate2 so we can find it with incore_match()
-        * above.
-        */
-       XFS_BUF_SET_FSPRIVATE2(bp, tp);
-
-       trace_xfs_trans_get_buf(bip);
+       _xfs_trans_bjoin(tp, bp, 1);
+       trace_xfs_trans_get_buf(bp->b_fspriv);
        return (bp);
 }
 
@@ -210,44 +237,11 @@ xfs_trans_getsb(xfs_trans_t       *tp,
        }
 
        bp = xfs_getsb(mp, flags);
-       if (bp == NULL) {
+       if (bp == NULL)
                return NULL;
-       }
-
-       /*
-        * The xfs_buf_log_item pointer is stored in b_fsprivate.  If
-        * it doesn't have one yet, then allocate one and initialize it.
-        * The checks to see if one is there are in xfs_buf_item_init().
-        */
-       xfs_buf_item_init(bp, mp);
-
-       /*
-        * Set the recursion count for the buffer within this transaction
-        * to 0.
-        */
-       bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
-       ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
-       ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
-       bip->bli_recur = 0;
-
-       /*
-        * Take a reference for this transaction on the buf item.
-        */
-       atomic_inc(&bip->bli_refcount);
-
-       /*
-        * Get a log_item_desc to point at the new item.
-        */
-       (void) xfs_trans_add_item(tp, (xfs_log_item_t*)bip);
-
-       /*
-        * Initialize b_fsprivate2 so we can find it with incore_match()
-        * above.
-        */
-       XFS_BUF_SET_FSPRIVATE2(bp, tp);
 
-       trace_xfs_trans_getsb(bip);
+       _xfs_trans_bjoin(tp, bp, 1);
+       trace_xfs_trans_getsb(bp->b_fspriv);
        return (bp);
 }
 
@@ -425,40 +419,9 @@ xfs_trans_read_buf(
        if (XFS_FORCED_SHUTDOWN(mp))
                goto shutdown_abort;
 
-       /*
-        * The xfs_buf_log_item pointer is stored in b_fsprivate.  If
-        * it doesn't have one yet, then allocate one and initialize it.
-        * The checks to see if one is there are in xfs_buf_item_init().
-        */
-       xfs_buf_item_init(bp, tp->t_mountp);
+       _xfs_trans_bjoin(tp, bp, 1);
+       trace_xfs_trans_read_buf(bp->b_fspriv);
 
-       /*
-        * Set the recursion count for the buffer within this transaction
-        * to 0.
-        */
-       bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
-       ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
-       ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
-       bip->bli_recur = 0;
-
-       /*
-        * Take a reference for this transaction on the buf item.
-        */
-       atomic_inc(&bip->bli_refcount);
-
-       /*
-        * Get a log_item_desc to point at the new item.
-        */
-       (void) xfs_trans_add_item(tp, (xfs_log_item_t*)bip);
-
-       /*
-        * Initialize b_fsprivate2 so we can find it with incore_match()
-        * above.
-        */
-       XFS_BUF_SET_FSPRIVATE2(bp, tp);
-
-       trace_xfs_trans_read_buf(bip);
        *bpp = bp;
        return 0;
 
@@ -622,53 +585,6 @@ xfs_trans_brelse(xfs_trans_t       *tp,
        return;
 }
 
-/*
- * Add the locked buffer to the transaction.
- * The buffer must be locked, and it cannot be associated with any
- * transaction.
- *
- * If the buffer does not yet have a buf log item associated with it,
- * then allocate one for it.  Then add the buf item to the transaction.
- */
-void
-xfs_trans_bjoin(xfs_trans_t    *tp,
-               xfs_buf_t       *bp)
-{
-       xfs_buf_log_item_t      *bip;
-
-       ASSERT(XFS_BUF_ISBUSY(bp));
-       ASSERT(XFS_BUF_FSPRIVATE2(bp, void *) == NULL);
-
-       /*
-        * The xfs_buf_log_item pointer is stored in b_fsprivate.  If
-        * it doesn't have one yet, then allocate one and initialize it.
-        * The checks to see if one is there are in xfs_buf_item_init().
-        */
-       xfs_buf_item_init(bp, tp->t_mountp);
-       bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
-       ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
-       ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
-       ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
-
-       /*
-        * Take a reference for this transaction on the buf item.
-        */
-       atomic_inc(&bip->bli_refcount);
-
-       /*
-        * Get a log_item_desc to point at the new item.
-        */
-       (void) xfs_trans_add_item(tp, (xfs_log_item_t *)bip);
-
-       /*
-        * Initialize b_fsprivate2 so we can find it with incore_match()
-        * in xfs_trans_get_buf() and friends above.
-        */
-       XFS_BUF_SET_FSPRIVATE2(bp, tp);
-
-       trace_xfs_trans_bjoin(bip);
-}
-
 /*
  * Mark the buffer as not needing to be unlocked when the buf item's
  * IOP_UNLOCK() routine is called.  The buffer must already be locked
index ddd2c5d1b854b73eacb8803bee64a75b9aff7f72..9d376be0ea389ee5b1cacd2f266fe3f44f2a7c2e 100644 (file)
@@ -583,113 +583,6 @@ xfs_readlink(
        return error;
 }
 
-/*
- * xfs_fsync
- *
- * This is called to sync the inode and its data out to disk.  We need to hold
- * the I/O lock while flushing the data, and the inode lock while flushing the
- * inode.  The inode lock CANNOT be held while flushing the data, so acquire
- * after we're done with that.
- */
-int
-xfs_fsync(
-       xfs_inode_t     *ip)
-{
-       xfs_trans_t     *tp;
-       int             error = 0;
-       int             log_flushed = 0;
-
-       xfs_itrace_entry(ip);
-
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-               return XFS_ERROR(EIO);
-
-       /*
-        * We always need to make sure that the required inode state is safe on
-        * disk.  The inode might be clean but we still might need to force the
-        * log because of committed transactions that haven't hit the disk yet.
-        * Likewise, there could be unflushed non-transactional changes to the
-        * inode core that have to go to disk and this requires us to issue
-        * a synchronous transaction to capture these changes correctly.
-        *
-        * This code relies on the assumption that if the update_* fields
-        * of the inode are clear and the inode is unpinned then it is clean
-        * and no action is required.
-        */
-       xfs_ilock(ip, XFS_ILOCK_SHARED);
-
-       if (!ip->i_update_core) {
-               /*
-                * Timestamps/size haven't changed since last inode flush or
-                * inode transaction commit.  That means either nothing got
-                * written or a transaction committed which caught the updates.
-                * If the latter happened and the transaction hasn't hit the
-                * disk yet, the inode will be still be pinned.  If it is,
-                * force the log.
-                */
-               xfs_iunlock(ip, XFS_ILOCK_SHARED);
-               if (xfs_ipincount(ip)) {
-                       if (ip->i_itemp->ili_last_lsn) {
-                               error = _xfs_log_force_lsn(ip->i_mount,
-                                               ip->i_itemp->ili_last_lsn,
-                                               XFS_LOG_SYNC, &log_flushed);
-                       } else {
-                               error = _xfs_log_force(ip->i_mount,
-                                               XFS_LOG_SYNC, &log_flushed);
-                       }
-               }
-       } else  {
-               /*
-                * Kick off a transaction to log the inode core to get the
-                * updates.  The sync transaction will also force the log.
-                */
-               xfs_iunlock(ip, XFS_ILOCK_SHARED);
-               tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_FSYNC_TS);
-               error = xfs_trans_reserve(tp, 0,
-                               XFS_FSYNC_TS_LOG_RES(ip->i_mount), 0, 0, 0);
-               if (error) {
-                       xfs_trans_cancel(tp, 0);
-                       return error;
-               }
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-
-               /*
-                * Note - it's possible that we might have pushed ourselves out
-                * of the way during trans_reserve which would flush the inode.
-                * But there's no guarantee that the inode buffer has actually
-                * gone out yet (it's delwri).  Plus the buffer could be pinned
-                * anyway if it's part of an inode in another recent
-                * transaction.  So we play it safe and fire off the
-                * transaction anyway.
-                */
-               xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
-               xfs_trans_ihold(tp, ip);
-               xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-               xfs_trans_set_sync(tp);
-               error = _xfs_trans_commit(tp, 0, &log_flushed);
-
-               xfs_iunlock(ip, XFS_ILOCK_EXCL);
-       }
-
-       if (ip->i_mount->m_flags & XFS_MOUNT_BARRIER) {
-               /*
-                * If the log write didn't issue an ordered tag we need
-                * to flush the disk cache for the data device now.
-                */
-               if (!log_flushed)
-                       xfs_blkdev_issue_flush(ip->i_mount->m_ddev_targp);
-
-               /*
-                * If this inode is on the RT dev we need to flush that
-                * cache as well.
-                */
-               if (XFS_IS_REALTIME_INODE(ip))
-                       xfs_blkdev_issue_flush(ip->i_mount->m_rtdev_targp);
-       }
-
-       return error;
-}
-
 /*
  * Flags for xfs_free_eofblocks
  */
index 774f40729ca13ea25e17a057a1b25606d7109ca0..d8dfa8d0dadd71eea4ba98f56c8323b2c5a02294 100644 (file)
@@ -21,7 +21,6 @@ int xfs_setattr(struct xfs_inode *ip, struct iattr *vap, int flags);
 #define XFS_ATTR_NOACL         0x08    /* Don't call xfs_acl_chmod */
 
 int xfs_readlink(struct xfs_inode *ip, char *link);
-int xfs_fsync(struct xfs_inode *ip);
 int xfs_release(struct xfs_inode *ip);
 int xfs_inactive(struct xfs_inode *ip);
 int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
@@ -50,18 +49,6 @@ int xfs_attr_set(struct xfs_inode *dp, const unsigned char *name,
 int xfs_attr_remove(struct xfs_inode *dp, const unsigned char *name, int flags);
 int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize,
                int flags, struct attrlist_cursor_kern *cursor);
-ssize_t xfs_read(struct xfs_inode *ip, struct kiocb *iocb,
-               const struct iovec *iovp, unsigned int segs,
-               loff_t *offset, int ioflags);
-ssize_t xfs_splice_read(struct xfs_inode *ip, struct file *infilp,
-               loff_t *ppos, struct pipe_inode_info *pipe, size_t count,
-               int flags, int ioflags);
-ssize_t xfs_splice_write(struct xfs_inode *ip,
-               struct pipe_inode_info *pipe, struct file *outfilp,
-               loff_t *ppos, size_t count, int flags, int ioflags);
-ssize_t xfs_write(struct xfs_inode *xip, struct kiocb *iocb,
-               const struct iovec *iovp, unsigned int nsegs,
-               loff_t *offset, int ioflags);
 int xfs_bmap(struct xfs_inode *ip, xfs_off_t offset, ssize_t count,
                int flags, struct xfs_iomap *iomapp, int *niomaps);
 void xfs_tosspages(struct xfs_inode *inode, xfs_off_t first,
@@ -72,4 +59,6 @@ int xfs_flush_pages(struct xfs_inode *ip, xfs_off_t first,
                xfs_off_t last, uint64_t flags, int fiopt);
 int xfs_wait_on_pages(struct xfs_inode *ip, xfs_off_t first, xfs_off_t last);
 
+int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
+
 #endif /* _XFS_VNODEOPS_H */
index 485eeb6c4ef3da76a15777943c62f890ea79b99f..979c6a57f2f1b48690d46171b21edb5c954f083b 100644 (file)
@@ -136,6 +136,32 @@ extern int __gpio_cansleep(unsigned gpio);
 
 extern int __gpio_to_irq(unsigned gpio);
 
+#define GPIOF_DIR_OUT  (0 << 0)
+#define GPIOF_DIR_IN   (1 << 0)
+
+#define GPIOF_INIT_LOW (0 << 1)
+#define GPIOF_INIT_HIGH        (1 << 1)
+
+#define GPIOF_IN               (GPIOF_DIR_IN)
+#define GPIOF_OUT_INIT_LOW     (GPIOF_DIR_OUT | GPIOF_INIT_LOW)
+#define GPIOF_OUT_INIT_HIGH    (GPIOF_DIR_OUT | GPIOF_INIT_HIGH)
+
+/**
+ * struct gpio - a structure describing a GPIO with configuration
+ * @gpio:      the GPIO number
+ * @flags:     GPIO configuration as specified by GPIOF_*
+ * @label:     a literal description string of this GPIO
+ */
+struct gpio {
+       unsigned        gpio;
+       unsigned long   flags;
+       const char      *label;
+};
+
+extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
+extern int gpio_request_array(struct gpio *array, size_t num);
+extern void gpio_free_array(struct gpio *array, size_t num);
+
 #ifdef CONFIG_GPIO_SYSFS
 
 /*
index ffac157fb5b2958aa33df80184a535a5fb5b9af6..4a3c4e441027be39da3d37b8a817ff9f918661dd 100644 (file)
@@ -801,6 +801,7 @@ struct drm_driver {
         */
        int (*gem_init_object) (struct drm_gem_object *obj);
        void (*gem_free_object) (struct drm_gem_object *obj);
+       void (*gem_free_object_unlocked) (struct drm_gem_object *obj);
 
        /* vga arb irq handler */
        void (*vgaarb_irq)(struct drm_device *dev, bool state);
@@ -1427,6 +1428,7 @@ extern void drm_sysfs_connector_remove(struct drm_connector *connector);
 int drm_gem_init(struct drm_device *dev);
 void drm_gem_destroy(struct drm_device *dev);
 void drm_gem_object_free(struct kref *kref);
+void drm_gem_object_free_unlocked(struct kref *kref);
 struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev,
                                            size_t size);
 void drm_gem_object_handle_free(struct kref *kref);
@@ -1443,10 +1445,15 @@ drm_gem_object_reference(struct drm_gem_object *obj)
 static inline void
 drm_gem_object_unreference(struct drm_gem_object *obj)
 {
-       if (obj == NULL)
-               return;
+       if (obj != NULL)
+               kref_put(&obj->refcount, drm_gem_object_free);
+}
 
-       kref_put(&obj->refcount, drm_gem_object_free);
+static inline void
+drm_gem_object_unreference_unlocked(struct drm_gem_object *obj)
+{
+       if (obj != NULL)
+               kref_put(&obj->refcount, drm_gem_object_free_unlocked);
 }
 
 int drm_gem_handle_create(struct drm_file *file_priv,
@@ -1475,6 +1482,21 @@ drm_gem_object_handle_unreference(struct drm_gem_object *obj)
        drm_gem_object_unreference(obj);
 }
 
+static inline void
+drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
+{
+       if (obj == NULL)
+               return;
+
+       /*
+       * Must bump handle count first as this may be the last
+       * ref, in which case the object would disappear before we
+       * checked for a name
+       */
+       kref_put(&obj->handlecount, drm_gem_object_handle_free);
+       drm_gem_object_unreference_unlocked(obj);
+}
+
 struct drm_gem_object *drm_gem_object_lookup(struct drm_device *dev,
                                             struct drm_file *filp,
                                             u32 handle);
diff --git a/include/drm/drm_buffer.h b/include/drm/drm_buffer.h
new file mode 100644 (file)
index 0000000..322dbff
--- /dev/null
@@ -0,0 +1,148 @@
+/**************************************************************************
+ *
+ * Copyright 2010 Pauli Nieminen.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+/*
+ * Multipart buffer for coping data which is larger than the page size.
+ *
+ * Authors:
+ * Pauli Nieminen <suokkos-at-gmail-dot-com>
+ */
+
+#ifndef _DRM_BUFFER_H_
+#define _DRM_BUFFER_H_
+
+#include "drmP.h"
+
+struct drm_buffer {
+       int iterator;
+       int size;
+       char *data[];
+};
+
+
+/**
+ * Return the index of page that buffer is currently pointing at.
+ */
+static inline int drm_buffer_page(struct drm_buffer *buf)
+{
+       return buf->iterator / PAGE_SIZE;
+}
+/**
+ * Return the index of the current byte in the page
+ */
+static inline int drm_buffer_index(struct drm_buffer *buf)
+{
+       return buf->iterator & (PAGE_SIZE - 1);
+}
+/**
+ * Return number of bytes that is left to process
+ */
+static inline int drm_buffer_unprocessed(struct drm_buffer *buf)
+{
+       return buf->size - buf->iterator;
+}
+
+/**
+ * Advance the buffer iterator number of bytes that is given.
+ */
+static inline void drm_buffer_advance(struct drm_buffer *buf, int bytes)
+{
+       buf->iterator += bytes;
+}
+
+/**
+ * Allocate the drm buffer object.
+ *
+ *   buf: A pointer to a pointer where the object is stored.
+ *   size: The number of bytes to allocate.
+ */
+extern int drm_buffer_alloc(struct drm_buffer **buf, int size);
+
+/**
+ * Copy the user data to the begin of the buffer and reset the processing
+ * iterator.
+ *
+ *   user_data: A pointer the data that is copied to the buffer.
+ *   size: The Number of bytes to copy.
+ */
+extern int drm_buffer_copy_from_user(struct drm_buffer *buf,
+               void __user *user_data, int size);
+
+/**
+ * Free the drm buffer object
+ */
+extern void drm_buffer_free(struct drm_buffer *buf);
+
+/**
+ * Read an object from buffer that may be split to multiple parts. If object
+ * is not split function just returns the pointer to object in buffer. But in
+ * case of split object data is copied to given stack object that is suplied
+ * by caller.
+ *
+ * The processing location of the buffer is also advanced to the next byte
+ * after the object.
+ *
+ *   objsize: The size of the objet in bytes.
+ *   stack_obj: A pointer to a memory location where object can be copied.
+ */
+extern void *drm_buffer_read_object(struct drm_buffer *buf,
+               int objsize, void *stack_obj);
+
+/**
+ * Returns the pointer to the dword which is offset number of elements from the
+ * current processing location.
+ *
+ * Caller must make sure that dword is not split in the buffer. This
+ * requirement is easily met if all the sizes of objects in buffer are
+ * multiples of dword and PAGE_SIZE is multiple dword.
+ *
+ * Call to this function doesn't change the processing location.
+ *
+ *   offset: The index of the dword relative to the internat iterator.
+ */
+static inline void *drm_buffer_pointer_to_dword(struct drm_buffer *buffer,
+               int offset)
+{
+       int iter = buffer->iterator + offset * 4;
+       return &buffer->data[iter / PAGE_SIZE][iter & (PAGE_SIZE - 1)];
+}
+/**
+ * Returns the pointer to the dword which is offset number of elements from
+ * the current processing location.
+ *
+ * Call to this function doesn't change the processing location.
+ *
+ *   offset: The index of the byte relative to the internat iterator.
+ */
+static inline void *drm_buffer_pointer_to_byte(struct drm_buffer *buffer,
+               int offset)
+{
+       int iter = buffer->iterator + offset;
+       return &buffer->data[iter / PAGE_SIZE][iter & (PAGE_SIZE - 1)];
+}
+
+#endif
index fdf43abc36dbe7ae36d0e690ab6e99b8a417f1d7..1347524a8e30e296da51b61b4f35e10e1708e56b 100644 (file)
@@ -801,4 +801,6 @@ extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev,
                                bool interlaced, int margins);
 extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);
+
+extern bool drm_edid_is_valid(struct edid *edid);
 #endif /* __DRM_CRTC_H__ */
index d33c3e038606c1354d671d90a9d3d776d4e225a7..b4209898f11526eb33dd39d53d03b92a6da7e1bb 100644 (file)
@@ -201,4 +201,7 @@ struct edid {
 
 #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
 
+/* define the number of Extension EDID block */
+#define DRM_MAX_EDID_EXT_NUM 4
+
 #endif /* __DRM_EDID_H__ */
index e6f3b120f51a5e4f9d51bf694b8ecba0b8d7503c..676104b7818c0d35cc69f6b60105891f29df63ee 100644 (file)
        {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6888, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6889, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x688A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68a0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68e1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68e5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68e8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68e9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68f1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68f8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68f9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
+       {0x1002, 0x68fe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x7100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x7101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x1002, 0x7102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
        {0x8086, 0x35e8, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
        {0x8086, 0x0042, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
        {0x8086, 0x0046, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
+       {0x8086, 0x0102, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, 0xffff00, 0}, \
        {0, 0, 0}
index f745948b61e480e31bca363a033f7a622487dabe..a6a9f4af5ebd8257d92dcd592551b6fb05f91931 100644 (file)
 #ifndef __NOUVEAU_DRM_H__
 #define __NOUVEAU_DRM_H__
 
-#define NOUVEAU_DRM_HEADER_PATCHLEVEL 15
+#define NOUVEAU_DRM_HEADER_PATCHLEVEL 16
 
 struct drm_nouveau_channel_alloc {
        uint32_t     fb_ctxdma_handle;
        uint32_t     tt_ctxdma_handle;
 
        int          channel;
+       uint32_t     pushbuf_domains;
 
        /* Notifier memory */
        uint32_t     notifier_handle;
@@ -109,68 +110,58 @@ struct drm_nouveau_gem_new {
        uint32_t align;
 };
 
+#define NOUVEAU_GEM_MAX_BUFFERS 1024
+struct drm_nouveau_gem_pushbuf_bo_presumed {
+       uint32_t valid;
+       uint32_t domain;
+       uint64_t offset;
+};
+
 struct drm_nouveau_gem_pushbuf_bo {
        uint64_t user_priv;
        uint32_t handle;
        uint32_t read_domains;
        uint32_t write_domains;
        uint32_t valid_domains;
-       uint32_t presumed_ok;
-       uint32_t presumed_domain;
-       uint64_t presumed_offset;
+       struct drm_nouveau_gem_pushbuf_bo_presumed presumed;
 };
 
 #define NOUVEAU_GEM_RELOC_LOW  (1 << 0)
 #define NOUVEAU_GEM_RELOC_HIGH (1 << 1)
 #define NOUVEAU_GEM_RELOC_OR   (1 << 2)
+#define NOUVEAU_GEM_MAX_RELOCS 1024
 struct drm_nouveau_gem_pushbuf_reloc {
+       uint32_t reloc_bo_index;
+       uint32_t reloc_bo_offset;
        uint32_t bo_index;
-       uint32_t reloc_index;
        uint32_t flags;
        uint32_t data;
        uint32_t vor;
        uint32_t tor;
 };
 
-#define NOUVEAU_GEM_MAX_BUFFERS 1024
-#define NOUVEAU_GEM_MAX_RELOCS 1024
+#define NOUVEAU_GEM_MAX_PUSH 512
+struct drm_nouveau_gem_pushbuf_push {
+       uint32_t bo_index;
+       uint32_t pad;
+       uint64_t offset;
+       uint64_t length;
+};
 
 struct drm_nouveau_gem_pushbuf {
        uint32_t channel;
-       uint32_t nr_dwords;
        uint32_t nr_buffers;
-       uint32_t nr_relocs;
-       uint64_t dwords;
        uint64_t buffers;
-       uint64_t relocs;
-};
-
-struct drm_nouveau_gem_pushbuf_call {
-       uint32_t channel;
-       uint32_t handle;
-       uint32_t offset;
-       uint32_t nr_buffers;
        uint32_t nr_relocs;
-       uint32_t nr_dwords;
-       uint64_t buffers;
+       uint32_t nr_push;
        uint64_t relocs;
+       uint64_t push;
        uint32_t suffix0;
        uint32_t suffix1;
-       /* below only accessed for CALL2 */
        uint64_t vram_available;
        uint64_t gart_available;
 };
 
-struct drm_nouveau_gem_pin {
-       uint32_t handle;
-       uint32_t domain;
-       uint64_t offset;
-};
-
-struct drm_nouveau_gem_unpin {
-       uint32_t handle;
-};
-
 #define NOUVEAU_GEM_CPU_PREP_NOWAIT                                  0x00000001
 #define NOUVEAU_GEM_CPU_PREP_NOBLOCK                                 0x00000002
 #define NOUVEAU_GEM_CPU_PREP_WRITE                                   0x00000004
@@ -183,14 +174,6 @@ struct drm_nouveau_gem_cpu_fini {
        uint32_t handle;
 };
 
-struct drm_nouveau_gem_tile {
-       uint32_t handle;
-       uint32_t offset;
-       uint32_t size;
-       uint32_t tile_mode;
-       uint32_t tile_flags;
-};
-
 enum nouveau_bus_type {
        NV_AGP     = 0,
        NV_PCI     = 1,
@@ -200,22 +183,17 @@ enum nouveau_bus_type {
 struct drm_nouveau_sarea {
 };
 
-#define DRM_NOUVEAU_CARD_INIT          0x00
-#define DRM_NOUVEAU_GETPARAM           0x01
-#define DRM_NOUVEAU_SETPARAM           0x02
-#define DRM_NOUVEAU_CHANNEL_ALLOC      0x03
-#define DRM_NOUVEAU_CHANNEL_FREE       0x04
-#define DRM_NOUVEAU_GROBJ_ALLOC        0x05
-#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC  0x06
-#define DRM_NOUVEAU_GPUOBJ_FREE        0x07
+#define DRM_NOUVEAU_GETPARAM           0x00
+#define DRM_NOUVEAU_SETPARAM           0x01
+#define DRM_NOUVEAU_CHANNEL_ALLOC      0x02
+#define DRM_NOUVEAU_CHANNEL_FREE       0x03
+#define DRM_NOUVEAU_GROBJ_ALLOC        0x04
+#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC  0x05
+#define DRM_NOUVEAU_GPUOBJ_FREE        0x06
 #define DRM_NOUVEAU_GEM_NEW            0x40
 #define DRM_NOUVEAU_GEM_PUSHBUF        0x41
-#define DRM_NOUVEAU_GEM_PUSHBUF_CALL   0x42
-#define DRM_NOUVEAU_GEM_PIN            0x43 /* !KMS only */
-#define DRM_NOUVEAU_GEM_UNPIN          0x44 /* !KMS only */
-#define DRM_NOUVEAU_GEM_CPU_PREP       0x45
-#define DRM_NOUVEAU_GEM_CPU_FINI       0x46
-#define DRM_NOUVEAU_GEM_INFO           0x47
-#define DRM_NOUVEAU_GEM_PUSHBUF_CALL2  0x48
+#define DRM_NOUVEAU_GEM_CPU_PREP       0x42
+#define DRM_NOUVEAU_GEM_CPU_FINI       0x43
+#define DRM_NOUVEAU_GEM_INFO           0x44
 
 #endif /* __NOUVEAU_DRM_H__ */
index 39537f3cf98ae89fca7b1d4e6fca5b18668735d5..81e614bf2dc3ac68accc19b825bf59bff066c913 100644 (file)
@@ -808,6 +808,7 @@ struct drm_radeon_gem_create {
 #define RADEON_TILING_SWAP_32BIT  0x8
 #define RADEON_TILING_SURFACE     0x10 /* this object requires a surface
                                        * when mapped - i.e. front buffer */
+#define RADEON_TILING_MICRO_SQUARE 0x20
 
 struct drm_radeon_gem_set_tiling {
        uint32_t        handle;
index 4c4e0f8375b397ff521455c2e92d9383cade07d3..e3f1b4a4b601aa5cb909030d733dad50f5c074cc 100644 (file)
@@ -908,7 +908,7 @@ extern int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
  * Utility function that returns the pgprot_t that should be used for
  * setting up a PTE with the caching model indicated by @c_state.
  */
-extern pgprot_t ttm_io_prot(enum ttm_caching_state c_state, pgprot_t tmp);
+extern pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp);
 
 #if (defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE)))
 #define TTM_HAS_AGP
index 3c7a358241a71d78c9aa288f08b1d83a61a1abb8..f391d45c8aea42eea13a21fcc3356313de63bd33 100644 (file)
@@ -424,7 +424,7 @@ extern void audit_syscall_exit(int failed, long return_code);
 extern void __audit_getname(const char *name);
 extern void audit_putname(const char *name);
 extern void __audit_inode(const char *name, const struct dentry *dentry);
-extern void __audit_inode_child(const char *dname, const struct dentry *dentry,
+extern void __audit_inode_child(const struct dentry *dentry,
                                const struct inode *parent);
 extern void __audit_ptrace(struct task_struct *t);
 
@@ -442,11 +442,10 @@ static inline void audit_inode(const char *name, const struct dentry *dentry) {
        if (unlikely(!audit_dummy_context()))
                __audit_inode(name, dentry);
 }
-static inline void audit_inode_child(const char *dname, 
-                                    const struct dentry *dentry,
+static inline void audit_inode_child(const struct dentry *dentry,
                                     const struct inode *parent) {
        if (unlikely(!audit_dummy_context()))
-               __audit_inode_child(dname, dentry, parent);
+               __audit_inode_child(dentry, parent);
 }
 void audit_core_dumps(long signr);
 
@@ -544,9 +543,9 @@ extern int audit_signals;
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
 #define __audit_inode(n,d) do { ; } while (0)
-#define __audit_inode_child(d,i,p) do { ; } while (0)
+#define __audit_inode_child(i,p) do { ; } while (0)
 #define audit_inode(n,d) do { ; } while (0)
-#define audit_inode_child(d,i,p) do { ; } while (0)
+#define audit_inode_child(i,p) do { ; } while (0)
 #define audit_core_dumps(i) do { ; } while (0)
 #define auditsc_get_stamp(c,t,s) (0)
 #define audit_get_loginuid(t) (-1)
index 89c6249fc561a0eaf38f5d01e850f70d52726dd5..c809e286d213df3cb1d63caa7f6b97b82162c34a 100644 (file)
@@ -74,6 +74,7 @@ struct coredump_params {
        struct pt_regs *regs;
        struct file *file;
        unsigned long limit;
+       unsigned long mm_flags;
 };
 
 /*
index 25b8b2f33ae947c0aedc9ab59c0ce88326da024c..b7938987923882e1e8853d63b31560a4aa0b0393 100644 (file)
  */
 #include <asm/bitops.h>
 
-#define for_each_bit(bit, addr, size) \
+#define for_each_set_bit(bit, addr, size) \
        for ((bit) = find_first_bit((addr), (size)); \
             (bit) < (size); \
             (bit) = find_next_bit((addr), (size), (bit) + 1))
 
+/* Temporary */
+#define for_each_bit(bit, addr, size) for_each_set_bit(bit, addr, size)
 
 static __inline__ int get_bitmask_order(unsigned int count)
 {
diff --git a/include/linux/btree-128.h b/include/linux/btree-128.h
new file mode 100644 (file)
index 0000000..0b3414c
--- /dev/null
@@ -0,0 +1,109 @@
+extern struct btree_geo btree_geo128;
+
+struct btree_head128 { struct btree_head h; };
+
+static inline void btree_init_mempool128(struct btree_head128 *head,
+                                        mempool_t *mempool)
+{
+       btree_init_mempool(&head->h, mempool);
+}
+
+static inline int btree_init128(struct btree_head128 *head)
+{
+       return btree_init(&head->h);
+}
+
+static inline void btree_destroy128(struct btree_head128 *head)
+{
+       btree_destroy(&head->h);
+}
+
+static inline void *btree_lookup128(struct btree_head128 *head, u64 k1, u64 k2)
+{
+       u64 key[2] = {k1, k2};
+       return btree_lookup(&head->h, &btree_geo128, (unsigned long *)&key);
+}
+
+static inline void *btree_get_prev128(struct btree_head128 *head,
+                                     u64 *k1, u64 *k2)
+{
+       u64 key[2] = {*k1, *k2};
+       void *val;
+
+       val = btree_get_prev(&head->h, &btree_geo128,
+                            (unsigned long *)&key);
+       *k1 = key[0];
+       *k2 = key[1];
+       return val;
+}
+
+static inline int btree_insert128(struct btree_head128 *head, u64 k1, u64 k2,
+                                 void *val, gfp_t gfp)
+{
+       u64 key[2] = {k1, k2};
+       return btree_insert(&head->h, &btree_geo128,
+                           (unsigned long *)&key, val, gfp);
+}
+
+static inline int btree_update128(struct btree_head128 *head, u64 k1, u64 k2,
+                                 void *val)
+{
+       u64 key[2] = {k1, k2};
+       return btree_update(&head->h, &btree_geo128,
+                           (unsigned long *)&key, val);
+}
+
+static inline void *btree_remove128(struct btree_head128 *head, u64 k1, u64 k2)
+{
+       u64 key[2] = {k1, k2};
+       return btree_remove(&head->h, &btree_geo128, (unsigned long *)&key);
+}
+
+static inline void *btree_last128(struct btree_head128 *head, u64 *k1, u64 *k2)
+{
+       u64 key[2];
+       void *val;
+
+       val = btree_last(&head->h, &btree_geo128, (unsigned long *)&key[0]);
+       if (val) {
+               *k1 = key[0];
+               *k2 = key[1];
+       }
+
+       return val;
+}
+
+static inline int btree_merge128(struct btree_head128 *target,
+                                struct btree_head128 *victim,
+                                gfp_t gfp)
+{
+       return btree_merge(&target->h, &victim->h, &btree_geo128, gfp);
+}
+
+void visitor128(void *elem, unsigned long opaque, unsigned long *__key,
+               size_t index, void *__func);
+
+typedef void (*visitor128_t)(void *elem, unsigned long opaque,
+                            u64 key1, u64 key2, size_t index);
+
+static inline size_t btree_visitor128(struct btree_head128 *head,
+                                     unsigned long opaque,
+                                     visitor128_t func2)
+{
+       return btree_visitor(&head->h, &btree_geo128, opaque,
+                            visitor128, func2);
+}
+
+static inline size_t btree_grim_visitor128(struct btree_head128 *head,
+                                          unsigned long opaque,
+                                          visitor128_t func2)
+{
+       return btree_grim_visitor(&head->h, &btree_geo128, opaque,
+                                 visitor128, func2);
+}
+
+#define btree_for_each_safe128(head, k1, k2, val)      \
+       for (val = btree_last128(head, &k1, &k2);       \
+            val;                                       \
+            val = btree_get_prev128(head, &k1, &k2))
+
diff --git a/include/linux/btree-type.h b/include/linux/btree-type.h
new file mode 100644 (file)
index 0000000..9a1147e
--- /dev/null
@@ -0,0 +1,147 @@
+#define __BTREE_TP(pfx, type, sfx)     pfx ## type ## sfx
+#define _BTREE_TP(pfx, type, sfx)      __BTREE_TP(pfx, type, sfx)
+#define BTREE_TP(pfx)                  _BTREE_TP(pfx, BTREE_TYPE_SUFFIX,)
+#define BTREE_FN(name)                 BTREE_TP(btree_ ## name)
+#define BTREE_TYPE_HEAD                        BTREE_TP(struct btree_head)
+#define VISITOR_FN                     BTREE_TP(visitor)
+#define VISITOR_FN_T                   _BTREE_TP(visitor, BTREE_TYPE_SUFFIX, _t)
+
+BTREE_TYPE_HEAD {
+       struct btree_head h;
+};
+
+static inline void BTREE_FN(init_mempool)(BTREE_TYPE_HEAD *head,
+                                         mempool_t *mempool)
+{
+       btree_init_mempool(&head->h, mempool);
+}
+
+static inline int BTREE_FN(init)(BTREE_TYPE_HEAD *head)
+{
+       return btree_init(&head->h);
+}
+
+static inline void BTREE_FN(destroy)(BTREE_TYPE_HEAD *head)
+{
+       btree_destroy(&head->h);
+}
+
+static inline int BTREE_FN(merge)(BTREE_TYPE_HEAD *target,
+                                 BTREE_TYPE_HEAD *victim,
+                                 gfp_t gfp)
+{
+       return btree_merge(&target->h, &victim->h, BTREE_TYPE_GEO, gfp);
+}
+
+#if (BITS_PER_LONG > BTREE_TYPE_BITS)
+static inline void *BTREE_FN(lookup)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key)
+{
+       unsigned long _key = key;
+       return btree_lookup(&head->h, BTREE_TYPE_GEO, &_key);
+}
+
+static inline int BTREE_FN(insert)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key,
+                                  void *val, gfp_t gfp)
+{
+       unsigned long _key = key;
+       return btree_insert(&head->h, BTREE_TYPE_GEO, &_key, val, gfp);
+}
+
+static inline int BTREE_FN(update)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key,
+               void *val)
+{
+       unsigned long _key = key;
+       return btree_update(&head->h, BTREE_TYPE_GEO, &_key, val);
+}
+
+static inline void *BTREE_FN(remove)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key)
+{
+       unsigned long _key = key;
+       return btree_remove(&head->h, BTREE_TYPE_GEO, &_key);
+}
+
+static inline void *BTREE_FN(last)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE *key)
+{
+       unsigned long _key;
+       void *val = btree_last(&head->h, BTREE_TYPE_GEO, &_key);
+       if (val)
+               *key = _key;
+       return val;
+}
+
+static inline void *BTREE_FN(get_prev)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE *key)
+{
+       unsigned long _key = *key;
+       void *val = btree_get_prev(&head->h, BTREE_TYPE_GEO, &_key);
+       if (val)
+               *key = _key;
+       return val;
+}
+#else
+static inline void *BTREE_FN(lookup)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key)
+{
+       return btree_lookup(&head->h, BTREE_TYPE_GEO, (unsigned long *)&key);
+}
+
+static inline int BTREE_FN(insert)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key,
+                          void *val, gfp_t gfp)
+{
+       return btree_insert(&head->h, BTREE_TYPE_GEO, (unsigned long *)&key,
+                           val, gfp);
+}
+
+static inline int BTREE_FN(update)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key,
+               void *val)
+{
+       return btree_update(&head->h, BTREE_TYPE_GEO, (unsigned long *)&key, val);
+}
+
+static inline void *BTREE_FN(remove)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE key)
+{
+       return btree_remove(&head->h, BTREE_TYPE_GEO, (unsigned long *)&key);
+}
+
+static inline void *BTREE_FN(last)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE *key)
+{
+       return btree_last(&head->h, BTREE_TYPE_GEO, (unsigned long *)key);
+}
+
+static inline void *BTREE_FN(get_prev)(BTREE_TYPE_HEAD *head, BTREE_KEYTYPE *key)
+{
+       return btree_get_prev(&head->h, BTREE_TYPE_GEO, (unsigned long *)key);
+}
+#endif
+
+void VISITOR_FN(void *elem, unsigned long opaque, unsigned long *key,
+               size_t index, void *__func);
+
+typedef void (*VISITOR_FN_T)(void *elem, unsigned long opaque,
+                            BTREE_KEYTYPE key, size_t index);
+
+static inline size_t BTREE_FN(visitor)(BTREE_TYPE_HEAD *head,
+                                      unsigned long opaque,
+                                      VISITOR_FN_T func2)
+{
+       return btree_visitor(&head->h, BTREE_TYPE_GEO, opaque,
+                            visitorl, func2);
+}
+
+static inline size_t BTREE_FN(grim_visitor)(BTREE_TYPE_HEAD *head,
+                                           unsigned long opaque,
+                                           VISITOR_FN_T func2)
+{
+       return btree_grim_visitor(&head->h, BTREE_TYPE_GEO, opaque,
+                                 visitorl, func2);
+}
+
+#undef VISITOR_FN
+#undef VISITOR_FN_T
+#undef __BTREE_TP
+#undef _BTREE_TP
+#undef BTREE_TP
+#undef BTREE_FN
+#undef BTREE_TYPE_HEAD
+#undef BTREE_TYPE_SUFFIX
+#undef BTREE_TYPE_GEO
+#undef BTREE_KEYTYPE
+#undef BTREE_TYPE_BITS
diff --git a/include/linux/btree.h b/include/linux/btree.h
new file mode 100644 (file)
index 0000000..65b5bb0
--- /dev/null
@@ -0,0 +1,243 @@
+#ifndef BTREE_H
+#define BTREE_H
+
+#include <linux/kernel.h>
+#include <linux/mempool.h>
+
+/**
+ * DOC: B+Tree basics
+ *
+ * A B+Tree is a data structure for looking up arbitrary (currently allowing
+ * unsigned long, u32, u64 and 2 * u64) keys into pointers. The data structure
+ * is described at http://en.wikipedia.org/wiki/B-tree, we currently do not
+ * use binary search to find the key on lookups.
+ *
+ * Each B+Tree consists of a head, that contains bookkeeping information and
+ * a variable number (starting with zero) nodes. Each node contains the keys
+ * and pointers to sub-nodes, or, for leaf nodes, the keys and values for the
+ * tree entries.
+ *
+ * Each node in this implementation has the following layout:
+ * [key1, key2, ..., keyN] [val1, val2, ..., valN]
+ *
+ * Each key here is an array of unsigned longs, geo->no_longs in total. The
+ * number of keys and values (N) is geo->no_pairs.
+ */
+
+/**
+ * struct btree_head - btree head
+ *
+ * @node: the first node in the tree
+ * @mempool: mempool used for node allocations
+ * @height: current of the tree
+ */
+struct btree_head {
+       unsigned long *node;
+       mempool_t *mempool;
+       int height;
+};
+
+/* btree geometry */
+struct btree_geo;
+
+/**
+ * btree_alloc - allocate function for the mempool
+ * @gfp_mask: gfp mask for the allocation
+ * @pool_data: unused
+ */
+void *btree_alloc(gfp_t gfp_mask, void *pool_data);
+
+/**
+ * btree_free - free function for the mempool
+ * @element: the element to free
+ * @pool_data: unused
+ */
+void btree_free(void *element, void *pool_data);
+
+/**
+ * btree_init_mempool - initialise a btree with given mempool
+ *
+ * @head: the btree head to initialise
+ * @mempool: the mempool to use
+ *
+ * When this function is used, there is no need to destroy
+ * the mempool.
+ */
+void btree_init_mempool(struct btree_head *head, mempool_t *mempool);
+
+/**
+ * btree_init - initialise a btree
+ *
+ * @head: the btree head to initialise
+ *
+ * This function allocates the memory pool that the
+ * btree needs. Returns zero or a negative error code
+ * (-%ENOMEM) when memory allocation fails.
+ *
+ */
+int __must_check btree_init(struct btree_head *head);
+
+/**
+ * btree_destroy - destroy mempool
+ *
+ * @head: the btree head to destroy
+ *
+ * This function destroys the internal memory pool, use only
+ * when using btree_init(), not with btree_init_mempool().
+ */
+void btree_destroy(struct btree_head *head);
+
+/**
+ * btree_lookup - look up a key in the btree
+ *
+ * @head: the btree to look in
+ * @geo: the btree geometry
+ * @key: the key to look up
+ *
+ * This function returns the value for the given key, or %NULL.
+ */
+void *btree_lookup(struct btree_head *head, struct btree_geo *geo,
+                  unsigned long *key);
+
+/**
+ * btree_insert - insert an entry into the btree
+ *
+ * @head: the btree to add to
+ * @geo: the btree geometry
+ * @key: the key to add (must not already be present)
+ * @val: the value to add (must not be %NULL)
+ * @gfp: allocation flags for node allocations
+ *
+ * This function returns 0 if the item could be added, or an
+ * error code if it failed (may fail due to memory pressure).
+ */
+int __must_check btree_insert(struct btree_head *head, struct btree_geo *geo,
+                             unsigned long *key, void *val, gfp_t gfp);
+/**
+ * btree_update - update an entry in the btree
+ *
+ * @head: the btree to update
+ * @geo: the btree geometry
+ * @key: the key to update
+ * @val: the value to change it to (must not be %NULL)
+ *
+ * This function returns 0 if the update was successful, or
+ * -%ENOENT if the key could not be found.
+ */
+int btree_update(struct btree_head *head, struct btree_geo *geo,
+                unsigned long *key, void *val);
+/**
+ * btree_remove - remove an entry from the btree
+ *
+ * @head: the btree to update
+ * @geo: the btree geometry
+ * @key: the key to remove
+ *
+ * This function returns the removed entry, or %NULL if the key
+ * could not be found.
+ */
+void *btree_remove(struct btree_head *head, struct btree_geo *geo,
+                  unsigned long *key);
+
+/**
+ * btree_merge - merge two btrees
+ *
+ * @target: the tree that gets all the entries
+ * @victim: the tree that gets merged into @target
+ * @geo: the btree geometry
+ * @gfp: allocation flags
+ *
+ * The two trees @target and @victim may not contain the same keys,
+ * that is a bug and triggers a BUG(). This function returns zero
+ * if the trees were merged successfully, and may return a failure
+ * when memory allocation fails, in which case both trees might have
+ * been partially merged, i.e. some entries have been moved from
+ * @victim to @target.
+ */
+int btree_merge(struct btree_head *target, struct btree_head *victim,
+               struct btree_geo *geo, gfp_t gfp);
+
+/**
+ * btree_last - get last entry in btree
+ *
+ * @head: btree head
+ * @geo: btree geometry
+ * @key: last key
+ *
+ * Returns the last entry in the btree, and sets @key to the key
+ * of that entry; returns NULL if the tree is empty, in that case
+ * key is not changed.
+ */
+void *btree_last(struct btree_head *head, struct btree_geo *geo,
+                unsigned long *key);
+
+/**
+ * btree_get_prev - get previous entry
+ *
+ * @head: btree head
+ * @geo: btree geometry
+ * @key: pointer to key
+ *
+ * The function returns the next item right before the value pointed to by
+ * @key, and updates @key with its key, or returns %NULL when there is no
+ * entry with a key smaller than the given key.
+ */
+void *btree_get_prev(struct btree_head *head, struct btree_geo *geo,
+                    unsigned long *key);
+
+
+/* internal use, use btree_visitor{l,32,64,128} */
+size_t btree_visitor(struct btree_head *head, struct btree_geo *geo,
+                    unsigned long opaque,
+                    void (*func)(void *elem, unsigned long opaque,
+                                 unsigned long *key, size_t index,
+                                 void *func2),
+                    void *func2);
+
+/* internal use, use btree_grim_visitor{l,32,64,128} */
+size_t btree_grim_visitor(struct btree_head *head, struct btree_geo *geo,
+                         unsigned long opaque,
+                         void (*func)(void *elem, unsigned long opaque,
+                                      unsigned long *key,
+                                      size_t index, void *func2),
+                         void *func2);
+
+
+#include <linux/btree-128.h>
+
+extern struct btree_geo btree_geo32;
+#define BTREE_TYPE_SUFFIX l
+#define BTREE_TYPE_BITS BITS_PER_LONG
+#define BTREE_TYPE_GEO &btree_geo32
+#define BTREE_KEYTYPE unsigned long
+#include <linux/btree-type.h>
+
+#define btree_for_each_safel(head, key, val)   \
+       for (val = btree_lastl(head, &key);     \
+            val;                               \
+            val = btree_get_prevl(head, &key))
+
+#define BTREE_TYPE_SUFFIX 32
+#define BTREE_TYPE_BITS 32
+#define BTREE_TYPE_GEO &btree_geo32
+#define BTREE_KEYTYPE u32
+#include <linux/btree-type.h>
+
+#define btree_for_each_safe32(head, key, val)  \
+       for (val = btree_last32(head, &key);    \
+            val;                               \
+            val = btree_get_prev32(head, &key))
+
+extern struct btree_geo btree_geo64;
+#define BTREE_TYPE_SUFFIX 64
+#define BTREE_TYPE_BITS 64
+#define BTREE_TYPE_GEO &btree_geo64
+#define BTREE_KEYTYPE u64
+#include <linux/btree-type.h>
+
+#define btree_for_each_safe64(head, key, val)  \
+       for (val = btree_last64(head, &key);    \
+            val;                               \
+            val = btree_get_prev64(head, &key))
+
+#endif
diff --git a/include/linux/coredump.h b/include/linux/coredump.h
new file mode 100644 (file)
index 0000000..b3c91d7
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _LINUX_COREDUMP_H
+#define _LINUX_COREDUMP_H
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * functions to write out all the necessary info.
+ */
+static inline int dump_write(struct file *file, const void *addr, int nr)
+{
+       return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
+}
+
+static inline int dump_seek(struct file *file, loff_t off)
+{
+       if (file->f_op->llseek && file->f_op->llseek != no_llseek) {
+               if (file->f_op->llseek(file, off, SEEK_CUR) < 0)
+                       return 0;
+       } else {
+               char *buf = (char *)get_zeroed_page(GFP_KERNEL);
+
+               if (!buf)
+                       return 0;
+               while (off > 0) {
+                       unsigned long n = off;
+
+                       if (n > PAGE_SIZE)
+                               n = PAGE_SIZE;
+                       if (!dump_write(file, buf, n))
+                               return 0;
+                       off -= n;
+               }
+               free_page((unsigned long)buf);
+       }
+       return 1;
+}
+
+#endif /* _LINUX_COREDUMP_H */
index dbcee7647d9a3a9c54dcf3c960d3828ad01ef314..bae6fe24d1f9de103d75d40b6439f7e17a406b2a 100644 (file)
@@ -90,10 +90,10 @@ extern const struct cpumask *const cpu_active_mask;
 #define cpu_present(cpu)       cpumask_test_cpu((cpu), cpu_present_mask)
 #define cpu_active(cpu)                cpumask_test_cpu((cpu), cpu_active_mask)
 #else
-#define num_online_cpus()      1
-#define num_possible_cpus()    1
-#define num_present_cpus()     1
-#define num_active_cpus()      1
+#define num_online_cpus()      1U
+#define num_possible_cpus()    1U
+#define num_present_cpus()     1U
+#define num_active_cpus()      1U
 #define cpu_online(cpu)                ((cpu) == 0)
 #define cpu_possible(cpu)      ((cpu) == 0)
 #define cpu_present(cpu)       ((cpu) == 0)
index d4c9c0b88adcea7fd8f9de8c5c315dcd64a68bca..1381cd97b4eda06c6154e6d0be04823979262b08 100644 (file)
@@ -118,10 +118,9 @@ struct dm_dev {
 /*
  * Constructors should call these functions to ensure destination devices
  * are opened/closed correctly.
- * FIXME: too many arguments.
  */
-int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
-                 sector_t len, fmode_t mode, struct dm_dev **result);
+int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+                                                struct dm_dev **result);
 void dm_put_device(struct dm_target *ti, struct dm_dev *d);
 
 /*
index b6bf17ee2f619601964d33d9c3e7cbf323f79317..5c9186b93fff0794488233df3afce65be8471e47 100644 (file)
@@ -37,14 +37,14 @@ enum dm_io_mem_type {
 struct dm_io_memory {
        enum dm_io_mem_type type;
 
+       unsigned offset;
+
        union {
                struct page_list *pl;
                struct bio_vec *bvec;
                void *vma;
                void *addr;
        } ptr;
-
-       unsigned offset;
 };
 
 struct dm_io_notify {
index aa95508d2f9547e23722d9b67f91b9705fe95033..2c445e11379026041cb795844711379546da2a20 100644 (file)
@@ -266,9 +266,9 @@ enum {
 #define DM_DEV_SET_GEOMETRY    _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
 #define DM_VERSION_MAJOR       4
-#define DM_VERSION_MINOR       16
+#define DM_VERSION_MINOR       17
 #define DM_VERSION_PATCHLEVEL  0
-#define DM_VERSION_EXTRA       "-ioctl (2009-11-05)"
+#define DM_VERSION_EXTRA       "-ioctl (2010-03-05)"
 
 /* Status bits */
 #define DM_READONLY_FLAG       (1 << 0) /* In/Out */
@@ -316,4 +316,9 @@ enum {
  */
 #define DM_QUERY_INACTIVE_TABLE_FLAG   (1 << 12) /* In */
 
+/*
+ * If set, a uevent was generated for which the caller may need to wait.
+ */
+#define DM_UEVENT_GENERATED_FLAG       (1 << 13) /* Out */
+
 #endif                         /* _LINUX_DM_IOCTL_H */
index 21fd9b7c6a40d1a519e1683ef798733625b151d7..20ea12c86fd007ce9cd66d084399621b4b323f3a 100644 (file)
@@ -31,6 +31,8 @@
  * if dma_cookie_t is >0 it's a DMA request cookie, <0 it's an error code
  */
 typedef s32 dma_cookie_t;
+#define DMA_MIN_COOKIE 1
+#define DMA_MAX_COOKIE INT_MAX
 
 #define dma_submit_error(cookie) ((cookie) < 0 ? 1 : 0)
 
index ad990c5f63f657180b4a72e627e25573653531fb..59785841805107a628a5210138d65b7061a9a1af 100644 (file)
@@ -50,6 +50,28 @@ typedef __s64        Elf64_Sxword;
 
 #define PT_GNU_STACK   (PT_LOOS + 0x474e551)
 
+/*
+ * Extended Numbering
+ *
+ * If the real number of program header table entries is larger than
+ * or equal to PN_XNUM(0xffff), it is set to sh_info field of the
+ * section header at index 0, and PN_XNUM is set to e_phnum
+ * field. Otherwise, the section header at index 0 is zero
+ * initialized, if it exists.
+ *
+ * Specifications are available in:
+ *
+ * - Sun microsystems: Linker and Libraries.
+ *   Part No: 817-1984-17, September 2008.
+ *   URL: http://docs.sun.com/app/docs/doc/817-1984
+ *
+ * - System V ABI AMD64 Architecture Processor Supplement
+ *   Draft Version 0.99.,
+ *   May 11, 2009.
+ *   URL: http://www.x86-64.org/
+ */
+#define PN_XNUM 0xffff
+
 /* These constants define the different elf file types */
 #define ET_NONE   0
 #define ET_REL    1
@@ -286,7 +308,7 @@ typedef struct elf64_phdr {
 #define SHN_COMMON     0xfff2
 #define SHN_HIRESERVE  0xffff
  
-typedef struct {
+typedef struct elf32_shdr {
   Elf32_Word   sh_name;
   Elf32_Word   sh_type;
   Elf32_Word   sh_flags;
@@ -394,16 +416,20 @@ typedef struct elf64_note {
 extern Elf32_Dyn _DYNAMIC [];
 #define elfhdr         elf32_hdr
 #define elf_phdr       elf32_phdr
+#define elf_shdr       elf32_shdr
 #define elf_note       elf32_note
 #define elf_addr_t     Elf32_Off
+#define Elf_Half       Elf32_Half
 
 #else
 
 extern Elf64_Dyn _DYNAMIC [];
 #define elfhdr         elf64_hdr
 #define elf_phdr       elf64_phdr
+#define elf_shdr       elf64_shdr
 #define elf_note       elf64_note
 #define elf_addr_t     Elf64_Off
+#define Elf_Half       Elf64_Half
 
 #endif
 
index 00d6a68d04215e7e289a3451ca4a8f714e2b6975..e687bc3ba4da0ec0830b507784bebb7ab8a5a918 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/user.h>
 #endif
 #include <linux/ptrace.h>
+#include <linux/elf.h>
+#include <linux/fs.h>
 
 struct elf_siginfo
 {
@@ -150,5 +152,20 @@ static inline int elf_core_copy_task_xfpregs(struct task_struct *t, elf_fpxregse
 
 #endif /* __KERNEL__ */
 
+/*
+ * These functions parameterize elf_core_dump in fs/binfmt_elf.c to write out
+ * extra segments containing the gate DSO contents.  Dumping its
+ * contents makes post-mortem fully interpretable later without matching up
+ * the same kernel and hardware config to see what PC values meant.
+ * Dumping its extra ELF program headers includes all the other information
+ * a debugger needs to easily find how the gate DSO was being used.
+ */
+extern Elf_Half elf_core_extra_phdrs(void);
+extern int
+elf_core_write_extra_phdrs(struct file *file, loff_t offset, size_t *size,
+                          unsigned long limit);
+extern int
+elf_core_write_extra_data(struct file *file, size_t *size, unsigned long limit);
+extern size_t elf_core_extra_data_size(void);
 
 #endif /* _LINUX_ELFCORE_H */
index dc12f416a49f6ed9054a1f59c0c91a9ea4f58d4e..a9cd507f8cd2c0bbf1d8fbe59f6272da07a7f88b 100644 (file)
@@ -96,6 +96,7 @@ struct fid {
  * @fh_to_parent:   find the implied object's parent and get a dentry for it
  * @get_name:       find the name for a given inode in a given directory
  * @get_parent:     find the parent of a given directory
+ * @commit_metadata: commit metadata changes to stable storage
  *
  * See Documentation/filesystems/nfs/Exporting for details on how to use
  * this interface correctly.
@@ -137,6 +138,9 @@ struct fid {
  *    is also a directory.  In the event that it cannot be found, or storage
  *    space cannot be allocated, a %ERR_PTR should be returned.
  *
+ * commit_metadata:
+ *    @commit_metadata should commit metadata changes to stable storage.
+ *
  * Locking rules:
  *    get_parent is called with child->d_inode->i_mutex down
  *    get_name is not (which is possibly inconsistent)
@@ -152,6 +156,7 @@ struct export_operations {
        int (*get_name)(struct dentry *parent, char *name,
                        struct dentry *child);
        struct dentry * (*get_parent)(struct dentry *child);
+       int (*commit_metadata)(struct inode *inode);
 };
 
 extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
index 6b049030fbe6f3ff063dfe7882712760af13b761..cac84b006667a42ee3296a3301e37f37978ec23f 100644 (file)
@@ -202,14 +202,6 @@ static inline __u32 ext3_mask_flags(umode_t mode, __u32 flags)
                return flags & EXT3_OTHER_FLMASK;
 }
 
-/*
- * Inode dynamic state flags
- */
-#define EXT3_STATE_JDATA               0x00000001 /* journaled data exists */
-#define EXT3_STATE_NEW                 0x00000002 /* inode is newly created */
-#define EXT3_STATE_XATTR               0x00000004 /* has in-inode xattrs */
-#define EXT3_STATE_FLUSH_ON_CLOSE      0x00000008
-
 /* Used to pass group descriptor data when online resize is done */
 struct ext3_new_group_input {
        __u32 group;            /* Group number for this data */
@@ -560,6 +552,31 @@ static inline int ext3_valid_inum(struct super_block *sb, unsigned long ino)
                (ino >= EXT3_FIRST_INO(sb) &&
                 ino <= le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count));
 }
+
+/*
+ * Inode dynamic state flags
+ */
+enum {
+       EXT3_STATE_JDATA,               /* journaled data exists */
+       EXT3_STATE_NEW,                 /* inode is newly created */
+       EXT3_STATE_XATTR,               /* has in-inode xattrs */
+       EXT3_STATE_FLUSH_ON_CLOSE,      /* flush dirty pages on close */
+};
+
+static inline int ext3_test_inode_state(struct inode *inode, int bit)
+{
+       return test_bit(bit, &EXT3_I(inode)->i_state);
+}
+
+static inline void ext3_set_inode_state(struct inode *inode, int bit)
+{
+       set_bit(bit, &EXT3_I(inode)->i_state);
+}
+
+static inline void ext3_clear_inode_state(struct inode *inode, int bit)
+{
+       clear_bit(bit, &EXT3_I(inode)->i_state);
+}
 #else
 /* Assume that user mode programs are passing in an ext3fs superblock, not
  * a kernel struct super_block.  This will allow us to call the feature-test
@@ -877,7 +894,7 @@ int ext3_get_blocks_handle(handle_t *handle, struct inode *inode,
        int create);
 
 extern struct inode *ext3_iget(struct super_block *, unsigned long);
-extern int  ext3_write_inode (struct inode *, int);
+extern int  ext3_write_inode (struct inode *, struct writeback_control *);
 extern int  ext3_setattr (struct dentry *, struct iattr *);
 extern void ext3_delete_inode (struct inode *);
 extern int  ext3_sync_inode (handle_t *, struct inode *);
index 93e7428156baf7e1c60ae0499b52d89f71119676..7679acdb519a14ae540f421a9d664b65914a5cfb 100644 (file)
@@ -87,7 +87,7 @@ struct ext3_inode_info {
         * near to their parent directory's inode.
         */
        __u32   i_block_group;
-       __u32   i_state;                /* Dynamic state flags for ext3 */
+       unsigned long   i_state;        /* Dynamic state flags for ext3 */
 
        /* block reservation info */
        struct ext3_block_alloc_info *i_block_alloc_info;
index 06ca9b21dad23b0d6753d0a92d6cbc2acba81f40..7b64ad40e4ceb8683a34ac0cdf76cdb9d4d1220f 100644 (file)
@@ -82,9 +82,10 @@ static inline void cleanup_fault_attr_dentries(struct fault_attr *attr)
 #endif /* CONFIG_FAULT_INJECTION */
 
 #ifdef CONFIG_FAILSLAB
-extern bool should_failslab(size_t size, gfp_t gfpflags);
+extern bool should_failslab(size_t size, gfp_t gfpflags, unsigned long flags);
 #else
-static inline bool should_failslab(size_t size, gfp_t gfpflags)
+static inline bool should_failslab(size_t size, gfp_t gfpflags,
+                               unsigned long flags)
 {
        return false;
 }
index 369767bd873efe5bafedb6b5936982952848a914..c10163b4c40e18e26fca70c76420fc8ceb10ad8b 100644 (file)
@@ -543,6 +543,8 @@ struct fb_cursor_user {
 #define FB_EVENT_GET_REQ                0x0D
 /*      Unbind from the console if possible */
 #define FB_EVENT_FB_UNBIND              0x0E
+/*      CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */
+#define FB_EVENT_REMAP_ALL_CONSOLE      0x0F
 
 struct fb_event {
        struct fb_info *info;
index 875451f1373a3ce38bfbfd81699eddd4c26ccecf..c6dcc1dfe78111bb1f32c127809ae956db789d10 100644 (file)
  */
 #ifdef CONFIG_FIRMWARE_MEMMAP
 
-int firmware_map_add(u64 start, u64 end, const char *type);
 int firmware_map_add_early(u64 start, u64 end, const char *type);
+int firmware_map_add_hotplug(u64 start, u64 end, const char *type);
 
 #else /* CONFIG_FIRMWARE_MEMMAP */
 
-static inline int firmware_map_add(u64 start, u64 end, const char *type)
+static inline int firmware_map_add_early(u64 start, u64 end, const char *type)
 {
        return 0;
 }
 
-static inline int firmware_map_add_early(u64 start, u64 end, const char *type)
+static inline int firmware_map_add_hotplug(u64 start, u64 end, const char *type)
 {
        return 0;
 }
index ebb1cd5bc2419c7fad8b421178021d52ac005fea..10b8dedcd18b82a82491129a2baaef0f18ca4ce1 100644 (file)
@@ -60,24 +60,24 @@ struct inodes_stat_t {
  */
 
 /* file is open for reading */
-#define FMODE_READ             ((__force fmode_t)1)
+#define FMODE_READ             ((__force fmode_t)0x1)
 /* file is open for writing */
-#define FMODE_WRITE            ((__force fmode_t)2)
+#define FMODE_WRITE            ((__force fmode_t)0x2)
 /* file is seekable */
-#define FMODE_LSEEK            ((__force fmode_t)4)
+#define FMODE_LSEEK            ((__force fmode_t)0x4)
 /* file can be accessed using pread */
-#define FMODE_PREAD            ((__force fmode_t)8)
+#define FMODE_PREAD            ((__force fmode_t)0x8)
 /* file can be accessed using pwrite */
-#define FMODE_PWRITE           ((__force fmode_t)16)
+#define FMODE_PWRITE           ((__force fmode_t)0x10)
 /* File is opened for execution with sys_execve / sys_uselib */
-#define FMODE_EXEC             ((__force fmode_t)32)
+#define FMODE_EXEC             ((__force fmode_t)0x20)
 /* File is opened with O_NDELAY (only set for block devices) */
-#define FMODE_NDELAY           ((__force fmode_t)64)
+#define FMODE_NDELAY           ((__force fmode_t)0x40)
 /* File is opened with O_EXCL (only set for block devices) */
-#define FMODE_EXCL             ((__force fmode_t)128)
+#define FMODE_EXCL             ((__force fmode_t)0x80)
 /* File is opened using open(.., 3, ..) and is writeable only for ioctls
    (specialy hack for floppy.c) */
-#define FMODE_WRITE_IOCTL      ((__force fmode_t)256)
+#define FMODE_WRITE_IOCTL      ((__force fmode_t)0x100)
 
 /*
  * Don't update ctime and mtime.
@@ -85,7 +85,10 @@ struct inodes_stat_t {
  * Currently a special hack for the XFS open_by_handle ioctl, but we'll
  * hopefully graduate it to a proper O_CMTIME flag supported by open(2) soon.
  */
-#define FMODE_NOCMTIME         ((__force fmode_t)2048)
+#define FMODE_NOCMTIME         ((__force fmode_t)0x800)
+
+/* Expect random access pattern */
+#define FMODE_RANDOM           ((__force fmode_t)0x1000)
 
 /*
  * The below are the various read and write types that we support. Some of
@@ -1305,6 +1308,8 @@ extern int send_sigurg(struct fown_struct *fown);
 #define MNT_FORCE      0x00000001      /* Attempt to forcibily umount */
 #define MNT_DETACH     0x00000002      /* Just detach from the tree */
 #define MNT_EXPIRE     0x00000004      /* Mark for expiry */
+#define UMOUNT_NOFOLLOW        0x00000008      /* Don't follow symlink on umount */
+#define UMOUNT_UNUSED  0x80000000      /* Flag guaranteed to be unused */
 
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
@@ -1314,9 +1319,9 @@ extern spinlock_t sb_lock;
 struct super_block {
        struct list_head        s_list;         /* Keep this first */
        dev_t                   s_dev;          /* search index; _not_ kdev_t */
-       unsigned long           s_blocksize;
-       unsigned char           s_blocksize_bits;
        unsigned char           s_dirt;
+       unsigned char           s_blocksize_bits;
+       unsigned long           s_blocksize;
        loff_t                  s_maxbytes;     /* Max file size */
        struct file_system_type *s_type;
        const struct super_operations   *s_op;
@@ -1357,16 +1362,16 @@ struct super_block {
        void                    *s_fs_info;     /* Filesystem private info */
        fmode_t                 s_mode;
 
+       /* Granularity of c/m/atime in ns.
+          Cannot be worse than a second */
+       u32                s_time_gran;
+
        /*
         * The next field is for VFS *only*. No filesystems have any business
         * even looking at it. You had been warned.
         */
        struct mutex s_vfs_rename_mutex;        /* Kludge */
 
-       /* Granularity of c/m/atime in ns.
-          Cannot be worse than a second */
-       u32                s_time_gran;
-
        /*
         * Filesystem subtype.  If non-empty the filesystem type field
         * in /proc/mounts will be "type.subtype"
@@ -1555,7 +1560,7 @@ struct super_operations {
        void (*destroy_inode)(struct inode *);
 
        void (*dirty_inode) (struct inode *);
-       int (*write_inode) (struct inode *, int);
+       int (*write_inode) (struct inode *, struct writeback_control *wbc);
        void (*drop_inode) (struct inode *);
        void (*delete_inode) (struct inode *);
        void (*put_super) (struct super_block *);
@@ -1794,7 +1799,8 @@ extern int may_umount(struct vfsmount *);
 extern long do_mount(char *, char *, char *, unsigned long, void *);
 extern struct vfsmount *collect_mounts(struct path *);
 extern void drop_collected_mounts(struct vfsmount *);
-
+extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
+                         struct vfsmount *);
 extern int vfs_statfs(struct dentry *, struct kstatfs *);
 
 extern int current_umask(void);
@@ -2058,12 +2064,6 @@ extern int invalidate_inodes(struct super_block *);
 unsigned long invalidate_mapping_pages(struct address_space *mapping,
                                        pgoff_t start, pgoff_t end);
 
-static inline unsigned long __deprecated
-invalidate_inode_pages(struct address_space *mapping)
-{
-       return invalidate_mapping_pages(mapping, 0, ~0UL);
-}
-
 static inline void invalidate_remote_inode(struct inode *inode)
 {
        if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
@@ -2132,6 +2132,7 @@ extern struct file * open_exec(const char *);
  
 /* fs/dcache.c -- generic fs support functions */
 extern int is_subdir(struct dentry *, struct dentry *);
+extern int path_is_under(struct path *, struct path *);
 extern ino_t find_inode_number(struct dentry *, struct qstr *);
 
 #include <linux/err.h>
@@ -2340,8 +2341,6 @@ extern int simple_rename(struct inode *, struct dentry *, struct inode *, struct
 extern int simple_sync_file(struct file *, struct dentry *, int);
 extern int simple_empty(struct dentry *);
 extern int simple_readpage(struct file *file, struct page *page);
-extern int simple_prepare_write(struct file *file, struct page *page,
-                       unsigned offset, unsigned to);
 extern int simple_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata);
index 936f9aa8bb97581e5ff798e1d92035556eaf88cd..df8fd9a3b214b5afde8a1368b327358117e6fbdb 100644 (file)
@@ -65,7 +65,7 @@ static inline void fsnotify_link_count(struct inode *inode)
  * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir
  */
 static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
-                                const char *old_name, const char *new_name,
+                                const char *old_name,
                                 int isdir, struct inode *target, struct dentry *moved)
 {
        struct inode *source = moved->d_inode;
@@ -73,6 +73,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
        u32 fs_cookie = fsnotify_get_cookie();
        __u32 old_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_FROM);
        __u32 new_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_TO);
+       const char *new_name = moved->d_name.name;
 
        if (old_dir == new_dir)
                old_dir_mask |= FS_DN_RENAME;
@@ -103,7 +104,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
                inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL);
                fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0);
        }
-       audit_inode_child(new_name, moved, new_dir);
+       audit_inode_child(moved, new_dir);
 }
 
 /*
@@ -146,7 +147,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
 {
        inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name,
                                  dentry->d_inode);
-       audit_inode_child(dentry->d_name.name, dentry, inode);
+       audit_inode_child(dentry, inode);
 
        fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0);
 }
@@ -161,7 +162,7 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct
        inotify_inode_queue_event(dir, IN_CREATE, 0, new_dentry->d_name.name,
                                  inode);
        fsnotify_link_count(inode);
-       audit_inode_child(new_dentry->d_name.name, new_dentry, dir);
+       audit_inode_child(new_dentry, dir);
 
        fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, new_dentry->d_name.name, 0);
 }
@@ -175,7 +176,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
        struct inode *d_inode = dentry->d_inode;
 
        inotify_inode_queue_event(inode, mask, 0, dentry->d_name.name, d_inode);
-       audit_inode_child(dentry->d_name.name, dentry, inode);
+       audit_inode_child(dentry, inode);
 
        fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0);
 }
index 557bdad320b624369441119e5f352ccbd41b04a6..4c6d41333f985a56807d19a67134e926e58b3542 100644 (file)
@@ -30,7 +30,8 @@ struct vm_area_struct;
  * _might_ fail.  This depends upon the particular VM implementation.
  *
  * __GFP_NOFAIL: The VM implementation _must_ retry infinitely: the caller
- * cannot handle allocation failures.
+ * cannot handle allocation failures.  This modifier is deprecated and no new
+ * users should be added.
  *
  * __GFP_NORETRY: The VM implementation must not retry indefinitely.
  *
@@ -83,6 +84,7 @@ struct vm_area_struct;
 #define GFP_HIGHUSER_MOVABLE   (__GFP_WAIT | __GFP_IO | __GFP_FS | \
                                 __GFP_HARDWALL | __GFP_HIGHMEM | \
                                 __GFP_MOVABLE)
+#define GFP_IOFS       (__GFP_IO | __GFP_FS)
 
 #ifdef CONFIG_NUMA
 #define GFP_THISNODE   (__GFP_THISNODE | __GFP_NOWARN | __GFP_NORETRY)
@@ -325,7 +327,7 @@ void free_pages_exact(void *virt, size_t size);
 
 extern void __free_pages(struct page *page, unsigned int order);
 extern void free_pages(unsigned long addr, unsigned int order);
-extern void free_hot_page(struct page *page);
+extern void free_hot_cold_page(struct page *page, int cold);
 
 #define __free_page(page) __free_pages((page), 0)
 #define free_page(addr) free_pages((addr),0)
@@ -337,9 +339,7 @@ void drain_local_pages(void *dummy);
 
 extern gfp_t gfp_allowed_mask;
 
-static inline void set_gfp_allowed_mask(gfp_t mask)
-{
-       gfp_allowed_mask = mask;
-}
+extern void set_gfp_allowed_mask(gfp_t mask);
+extern gfp_t clear_gfp_allowed_mask(gfp_t mask);
 
 #endif /* __LINUX_GFP_H */
index 81736d6a8db7380280a8da7757ca017ed2ce08e4..d5c5a60c8a0b1a2d513b8c19e060c4b8d1d94bd5 100644 (file)
@@ -1,3 +1,9 @@
+#ifndef _LINUX_PCA953X_H
+#define _LINUX_PCA953X_H
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
 /* platform data for the PCA9539 16-bit I/O expander driver */
 
 struct pca953x_platform_data {
@@ -7,6 +13,9 @@ struct pca953x_platform_data {
        /* initial polarity inversion setting */
        uint16_t        invert;
 
+       /* interrupt base */
+       int             irq_base;
+
        void            *context;       /* param to setup/teardown */
 
        int             (*setup)(struct i2c_client *client,
@@ -17,3 +26,5 @@ struct pca953x_platform_data {
                                void *context);
        char            **names;
 };
+
+#endif /* _LINUX_PCA953X_H */
index 0ec612959042dbfece0dbac4dbc33c11c4da43f5..97e6ab43518469354dcebc3703d2aa7fa9c4ff5f 100644 (file)
@@ -515,6 +515,8 @@ struct ide_drive_s {
         u8     init_speed;     /* transfer rate set at boot */
         u8     current_speed;  /* current transfer rate set */
        u8      desired_speed;  /* desired transfer rate set */
+       u8      pio_mode;       /* for ->set_pio_mode _only_ */
+       u8      dma_mode;       /* for ->dma_pio_mode _only_ */
         u8     dn;             /* now wide spread use */
        u8      acoustic;       /* acoustic management */
        u8      media;          /* disk, cdrom, tape, floppy, ... */
@@ -622,8 +624,8 @@ extern const struct ide_tp_ops default_tp_ops;
  */
 struct ide_port_ops {
        void    (*init_dev)(ide_drive_t *);
-       void    (*set_pio_mode)(ide_drive_t *, const u8);
-       void    (*set_dma_mode)(ide_drive_t *, const u8);
+       void    (*set_pio_mode)(struct hwif_s *, ide_drive_t *);
+       void    (*set_dma_mode)(struct hwif_s *, ide_drive_t *);
        int     (*reset_poll)(ide_drive_t *);
        void    (*pre_reset)(ide_drive_t *);
        void    (*resetproc)(ide_drive_t *);
@@ -1494,7 +1496,6 @@ int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int);
 #ifdef CONFIG_IDE_XFER_MODE
 int ide_scan_pio_blacklist(char *);
 const char *ide_xfer_verbose(u8);
-u8 ide_get_best_pio_mode(ide_drive_t *, u8, u8);
 int ide_pio_need_iordy(ide_drive_t *, const u8);
 int ide_set_pio_mode(ide_drive_t *, u8);
 int ide_set_dma_mode(ide_drive_t *, u8);
index 331530cd3cc6563f8e2f12b35ef544d2763788e1..f3aa59cb675d84cc92308bf038661826679cf5f0 100644 (file)
@@ -246,19 +246,8 @@ typedef struct journal_superblock_s
 
 #define J_ASSERT(assert)       BUG_ON(!(assert))
 
-#if defined(CONFIG_BUFFER_DEBUG)
-void buffer_assertion_failure(struct buffer_head *bh);
-#define J_ASSERT_BH(bh, expr)                                          \
-       do {                                                            \
-               if (!(expr))                                            \
-                       buffer_assertion_failure(bh);                   \
-               J_ASSERT(expr);                                         \
-       } while (0)
-#define J_ASSERT_JH(jh, expr)  J_ASSERT_BH(jh2bh(jh), expr)
-#else
 #define J_ASSERT_BH(bh, expr)  J_ASSERT(expr)
 #define J_ASSERT_JH(jh, expr)  J_ASSERT(expr)
-#endif
 
 #if defined(JBD_PARANOID_IOFAIL)
 #define J_EXPECT(expr, why...)         J_ASSERT(expr)
index 638ce4554c76e903d632720b3d25dea3350c745b..1ec876358180ecd72ec00f06de81af9d6e4bba71 100644 (file)
@@ -69,15 +69,8 @@ extern u8 jbd2_journal_enable_debug;
 #define jbd_debug(f, a...)     /**/
 #endif
 
-static inline void *jbd2_alloc(size_t size, gfp_t flags)
-{
-       return (void *)__get_free_pages(flags, get_order(size));
-}
-
-static inline void jbd2_free(void *ptr, size_t size)
-{
-       free_pages((unsigned long)ptr, get_order(size));
-};
+extern void *jbd2_alloc(size_t size, gfp_t flags);
+extern void jbd2_free(void *ptr, size_t size);
 
 #define JBD2_MIN_JOURNAL_BLOCKS 1024
 
@@ -284,19 +277,8 @@ typedef struct journal_superblock_s
 
 #define J_ASSERT(assert)       BUG_ON(!(assert))
 
-#if defined(CONFIG_BUFFER_DEBUG)
-void buffer_assertion_failure(struct buffer_head *bh);
-#define J_ASSERT_BH(bh, expr)                                          \
-       do {                                                            \
-               if (!(expr))                                            \
-                       buffer_assertion_failure(bh);                   \
-               J_ASSERT(expr);                                         \
-       } while (0)
-#define J_ASSERT_JH(jh, expr)  J_ASSERT_BH(jh2bh(jh), expr)
-#else
 #define J_ASSERT_BH(bh, expr)  J_ASSERT(expr)
 #define J_ASSERT_JH(jh, expr)  J_ASSERT(expr)
-#endif
 
 #if defined(JBD2_PARANOID_IOFAIL)
 #define J_EXPECT(expr, why...)         J_ASSERT(expr)
index 1b672f74a32f0d76d27c277c759c6c3a24135674..e7d1b2e0070d3570b7022877a79fa2f0ed081507 100644 (file)
@@ -122,6 +122,11 @@ struct kprobe {
 /* Kprobe status flags */
 #define KPROBE_FLAG_GONE       1 /* breakpoint has already gone */
 #define KPROBE_FLAG_DISABLED   2 /* probe is temporarily disabled */
+#define KPROBE_FLAG_OPTIMIZED  4 /*
+                                  * probe is really optimized.
+                                  * NOTE:
+                                  * this flag is only for optimized_kprobe.
+                                  */
 
 /* Has this kprobe gone ? */
 static inline int kprobe_gone(struct kprobe *p)
@@ -134,6 +139,12 @@ static inline int kprobe_disabled(struct kprobe *p)
 {
        return p->flags & (KPROBE_FLAG_DISABLED | KPROBE_FLAG_GONE);
 }
+
+/* Is this kprobe really running optimized path ? */
+static inline int kprobe_optimized(struct kprobe *p)
+{
+       return p->flags & KPROBE_FLAG_OPTIMIZED;
+}
 /*
  * Special probe type that uses setjmp-longjmp type tricks to resume
  * execution at a specified entry with a matching prototype corresponding
@@ -249,6 +260,39 @@ extern kprobe_opcode_t *get_insn_slot(void);
 extern void free_insn_slot(kprobe_opcode_t *slot, int dirty);
 extern void kprobes_inc_nmissed_count(struct kprobe *p);
 
+#ifdef CONFIG_OPTPROBES
+/*
+ * Internal structure for direct jump optimized probe
+ */
+struct optimized_kprobe {
+       struct kprobe kp;
+       struct list_head list;  /* list for optimizing queue */
+       struct arch_optimized_insn optinsn;
+};
+
+/* Architecture dependent functions for direct jump optimization */
+extern int arch_prepared_optinsn(struct arch_optimized_insn *optinsn);
+extern int arch_check_optimized_kprobe(struct optimized_kprobe *op);
+extern int arch_prepare_optimized_kprobe(struct optimized_kprobe *op);
+extern void arch_remove_optimized_kprobe(struct optimized_kprobe *op);
+extern int  arch_optimize_kprobe(struct optimized_kprobe *op);
+extern void arch_unoptimize_kprobe(struct optimized_kprobe *op);
+extern kprobe_opcode_t *get_optinsn_slot(void);
+extern void free_optinsn_slot(kprobe_opcode_t *slot, int dirty);
+extern int arch_within_optimized_kprobe(struct optimized_kprobe *op,
+                                       unsigned long addr);
+
+extern void opt_pre_handler(struct kprobe *p, struct pt_regs *regs);
+
+#ifdef CONFIG_SYSCTL
+extern int sysctl_kprobes_optimization;
+extern int proc_kprobes_optimization_handler(struct ctl_table *table,
+                                            int write, void __user *buffer,
+                                            size_t *length, loff_t *ppos);
+#endif
+
+#endif /* CONFIG_OPTPROBES */
+
 /* Get the kprobe at this addr (if any) - called with preemption disabled */
 struct kprobe *get_kprobe(void *addr);
 void kretprobe_hash_lock(struct task_struct *tsk,
index a24de0b1858e9ad6080a353c4594cb5c3804703d..60df9c84ecae2e0606d1ffc3cc8c9ec7d79785d9 100644 (file)
@@ -103,7 +103,7 @@ struct kvm_userspace_memory_region {
 
 /* for kvm_memory_region::flags */
 #define KVM_MEM_LOG_DIRTY_PAGES  1UL
-
+#define KVM_MEMSLOT_INVALID      (1UL << 1)
 
 /* for KVM_IRQ_LINE */
 struct kvm_irq_level {
@@ -497,6 +497,11 @@ struct kvm_ioeventfd {
 #endif
 #define KVM_CAP_S390_PSW 42
 #define KVM_CAP_PPC_SEGSTATE 43
+#define KVM_CAP_HYPERV 44
+#define KVM_CAP_HYPERV_VAPIC 45
+#define KVM_CAP_HYPERV_SPIN 46
+#define KVM_CAP_PCI_SEGMENT 47
+#define KVM_CAP_X86_ROBUST_SINGLESTEP 51
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -691,8 +696,9 @@ struct kvm_assigned_pci_dev {
        __u32 busnr;
        __u32 devfn;
        __u32 flags;
+       __u32 segnr;
        union {
-               __u32 reserved[12];
+               __u32 reserved[11];
        };
 };
 
index bd5a616d937318dffb2a88ee0465873aef44adc7..a3fd0f91d943c8ad2b7264812a554ff6ebfc9a6e 100644 (file)
@@ -38,6 +38,7 @@
 #define KVM_REQ_MMU_SYNC           7
 #define KVM_REQ_KVMCLOCK_UPDATE    8
 #define KVM_REQ_KICK               9
+#define KVM_REQ_DEACTIVATE_FPU    10
 
 #define KVM_USERSPACE_IRQ_SOURCE_ID    0
 
@@ -57,20 +58,20 @@ struct kvm_io_bus {
        struct kvm_io_device *devs[NR_IOBUS_DEVS];
 };
 
-void kvm_io_bus_init(struct kvm_io_bus *bus);
-void kvm_io_bus_destroy(struct kvm_io_bus *bus);
-int kvm_io_bus_write(struct kvm_io_bus *bus, gpa_t addr, int len,
-                    const void *val);
-int kvm_io_bus_read(struct kvm_io_bus *bus, gpa_t addr, int len,
+enum kvm_bus {
+       KVM_MMIO_BUS,
+       KVM_PIO_BUS,
+       KVM_NR_BUSES
+};
+
+int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
+                    int len, const void *val);
+int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len,
                    void *val);
-int __kvm_io_bus_register_dev(struct kvm_io_bus *bus,
-                              struct kvm_io_device *dev);
-int kvm_io_bus_register_dev(struct kvm *kvm, struct kvm_io_bus *bus,
+int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx,
                            struct kvm_io_device *dev);
-void __kvm_io_bus_unregister_dev(struct kvm_io_bus *bus,
-                                struct kvm_io_device *dev);
-void kvm_io_bus_unregister_dev(struct kvm *kvm, struct kvm_io_bus *bus,
-                              struct kvm_io_device *dev);
+int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+                             struct kvm_io_device *dev);
 
 struct kvm_vcpu {
        struct kvm *kvm;
@@ -83,6 +84,8 @@ struct kvm_vcpu {
        struct kvm_run *run;
        unsigned long requests;
        unsigned long guest_debug;
+       int srcu_idx;
+
        int fpu_active;
        int guest_fpu_loaded;
        wait_queue_head_t wq;
@@ -150,14 +153,19 @@ struct kvm_irq_routing_table {};
 
 #endif
 
-struct kvm {
-       spinlock_t mmu_lock;
-       spinlock_t requests_lock;
-       struct rw_semaphore slots_lock;
-       struct mm_struct *mm; /* userspace tied to this vm */
+struct kvm_memslots {
        int nmemslots;
        struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS +
                                        KVM_PRIVATE_MEM_SLOTS];
+};
+
+struct kvm {
+       spinlock_t mmu_lock;
+       raw_spinlock_t requests_lock;
+       struct mutex slots_lock;
+       struct mm_struct *mm; /* userspace tied to this vm */
+       struct kvm_memslots *memslots;
+       struct srcu_struct srcu;
 #ifdef CONFIG_KVM_APIC_ARCHITECTURE
        u32 bsp_vcpu_id;
        struct kvm_vcpu *bsp_vcpu;
@@ -166,8 +174,7 @@ struct kvm {
        atomic_t online_vcpus;
        struct list_head vm_list;
        struct mutex lock;
-       struct kvm_io_bus mmio_bus;
-       struct kvm_io_bus pio_bus;
+       struct kvm_io_bus *buses[KVM_NR_BUSES];
 #ifdef CONFIG_HAVE_KVM_EVENTFD
        struct {
                spinlock_t        lock;
@@ -249,13 +256,20 @@ int kvm_set_memory_region(struct kvm *kvm,
 int __kvm_set_memory_region(struct kvm *kvm,
                            struct kvm_userspace_memory_region *mem,
                            int user_alloc);
-int kvm_arch_set_memory_region(struct kvm *kvm,
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+                               struct kvm_memory_slot *memslot,
+                               struct kvm_memory_slot old,
+                               struct kvm_userspace_memory_region *mem,
+                               int user_alloc);
+void kvm_arch_commit_memory_region(struct kvm *kvm,
                                struct kvm_userspace_memory_region *mem,
                                struct kvm_memory_slot old,
                                int user_alloc);
 void kvm_disable_largepages(void);
 void kvm_arch_flush_shadow(struct kvm *kvm);
 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn);
+gfn_t unalias_gfn_instantiation(struct kvm *kvm, gfn_t gfn);
+
 struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn);
 unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn);
 void kvm_release_page_clean(struct page *page);
@@ -264,6 +278,9 @@ void kvm_set_page_dirty(struct page *page);
 void kvm_set_page_accessed(struct page *page);
 
 pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn);
+pfn_t gfn_to_pfn_memslot(struct kvm *kvm,
+                        struct kvm_memory_slot *slot, gfn_t gfn);
+int memslot_id(struct kvm *kvm, gfn_t gfn);
 void kvm_release_pfn_dirty(pfn_t);
 void kvm_release_pfn_clean(pfn_t pfn);
 void kvm_set_pfn_dirty(pfn_t pfn);
@@ -283,6 +300,7 @@ int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
 int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
 struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
 int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn);
+unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn);
 void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
 
 void kvm_vcpu_block(struct kvm_vcpu *vcpu);
@@ -383,6 +401,7 @@ struct kvm_assigned_dev_kernel {
        struct work_struct interrupt_work;
        struct list_head list;
        int assigned_dev_id;
+       int host_segnr;
        int host_busnr;
        int host_devfn;
        unsigned int entries_nr;
@@ -429,8 +448,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id);
 #define KVM_IOMMU_CACHE_COHERENCY      0x1
 
 #ifdef CONFIG_IOMMU_API
-int kvm_iommu_map_pages(struct kvm *kvm, gfn_t base_gfn,
-                       unsigned long npages);
+int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot);
 int kvm_iommu_map_guest(struct kvm *kvm);
 int kvm_iommu_unmap_guest(struct kvm *kvm);
 int kvm_assign_device(struct kvm *kvm,
@@ -480,11 +498,6 @@ static inline void kvm_guest_exit(void)
        current->flags &= ~PF_VCPU;
 }
 
-static inline int memslot_id(struct kvm *kvm, struct kvm_memory_slot *slot)
-{
-       return slot - kvm->memslots;
-}
-
 static inline gpa_t gfn_to_gpa(gfn_t gfn)
 {
        return (gpa_t)gfn << PAGE_SHIFT;
@@ -532,6 +545,10 @@ static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_se
 }
 #endif
 
+#ifndef KVM_ARCH_HAS_UNALIAS_INSTANTIATION
+#define unalias_gfn_instantiation unalias_gfn
+#endif
+
 #ifdef CONFIG_HAVE_KVM_IRQCHIP
 
 #define KVM_MAX_IRQ_ROUTES 1024
index 5d9c6558e8abf44d19326170e834aaac079581e0..8392884a2977cda6a5f004fed2511ad51a140aa5 100644 (file)
@@ -498,7 +498,7 @@ static inline void list_splice_tail_init(struct list_head *list,
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
 /**
- * list_for_each_entry_safe_continue
+ * list_for_each_entry_safe_continue - continue list iteration safe against removal
  * @pos:       the type * to use as a loop cursor.
  * @n:         another type * to use as temporary storage
  * @head:      the head for your list.
@@ -514,7 +514,7 @@ static inline void list_splice_tail_init(struct list_head *list,
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
 /**
- * list_for_each_entry_safe_from
+ * list_for_each_entry_safe_from - iterate over list from current point safe against removal
  * @pos:       the type * to use as a loop cursor.
  * @n:         another type * to use as temporary storage
  * @head:      the head for your list.
@@ -529,7 +529,7 @@ static inline void list_splice_tail_init(struct list_head *list,
             pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
 /**
- * list_for_each_entry_safe_reverse
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
  * @pos:       the type * to use as a loop cursor.
  * @n:         another type * to use as temporary storage
  * @head:      the head for your list.
index 76285e01b39ee1641336989ea99d727272645d0b..eb9800f05782413f640a0b3fcf68dbf1aa5ca7fa 100644 (file)
@@ -52,7 +52,6 @@
 #define CGROUP_SUPER_MAGIC     0x27e0eb
 
 #define FUTEXFS_SUPER_MAGIC    0xBAD1DEA
-#define INOTIFYFS_SUPER_MAGIC  0x2BAD1DEA
 
 #define STACK_END_MAGIC                0x57AC6E9D
 
index 35680409b8cfd3674c0a319e252fbb204ca5e1c9..8895d9d8879cb12b164d28f5f22da38905d122cb 100644 (file)
@@ -26,10 +26,30 @@ int mc13783_irq_request(struct mc13783 *mc13783, int irq,
 int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq,
                irq_handler_t handler, const char *name, void *dev);
 int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev);
-int mc13783_ackirq(struct mc13783 *mc13783, int irq);
 
-int mc13783_mask(struct mc13783 *mc13783, int irq);
-int mc13783_unmask(struct mc13783 *mc13783, int irq);
+int mc13783_irq_mask(struct mc13783 *mc13783, int irq);
+int mc13783_irq_unmask(struct mc13783 *mc13783, int irq);
+int mc13783_irq_status(struct mc13783 *mc13783, int irq,
+               int *enabled, int *pending);
+int mc13783_irq_ack(struct mc13783 *mc13783, int irq);
+
+static inline int mc13783_mask(struct mc13783 *mc13783, int irq) __deprecated;
+static inline int mc13783_mask(struct mc13783 *mc13783, int irq)
+{
+       return mc13783_irq_mask(mc13783, irq);
+}
+
+static inline int mc13783_unmask(struct mc13783 *mc13783, int irq) __deprecated;
+static inline int mc13783_unmask(struct mc13783 *mc13783, int irq)
+{
+       return mc13783_irq_unmask(mc13783, irq);
+}
+
+static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq) __deprecated;
+static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq)
+{
+       return mc13783_irq_ack(mc13783, irq);
+}
 
 #define MC13783_ADC0           43
 #define MC13783_ADC0_ADREFEN           (1 << 10)
@@ -108,6 +128,8 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
 #define        MC13783_REGU_V2         28
 #define        MC13783_REGU_V3         29
 #define        MC13783_REGU_V4         30
+#define        MC13783_REGU_PWGT1SPI   31
+#define        MC13783_REGU_PWGT2SPI   32
 
 #define MC13783_IRQ_ADCDONE    0
 #define MC13783_IRQ_ADCBISDONE 1
index 90957f14195c7685e0ad25e188f2d2328ffbec97..3899395a03dea678551ed6a0a365b26d03088b6d 100644 (file)
@@ -870,6 +870,108 @@ extern int mprotect_fixup(struct vm_area_struct *vma,
  */
 int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
                          struct page **pages);
+/*
+ * per-process(per-mm_struct) statistics.
+ */
+#if defined(SPLIT_RSS_COUNTING)
+/*
+ * The mm counters are not protected by its page_table_lock,
+ * so must be incremented atomically.
+ */
+static inline void set_mm_counter(struct mm_struct *mm, int member, long value)
+{
+       atomic_long_set(&mm->rss_stat.count[member], value);
+}
+
+unsigned long get_mm_counter(struct mm_struct *mm, int member);
+
+static inline void add_mm_counter(struct mm_struct *mm, int member, long value)
+{
+       atomic_long_add(value, &mm->rss_stat.count[member]);
+}
+
+static inline void inc_mm_counter(struct mm_struct *mm, int member)
+{
+       atomic_long_inc(&mm->rss_stat.count[member]);
+}
+
+static inline void dec_mm_counter(struct mm_struct *mm, int member)
+{
+       atomic_long_dec(&mm->rss_stat.count[member]);
+}
+
+#else  /* !USE_SPLIT_PTLOCKS */
+/*
+ * The mm counters are protected by its page_table_lock,
+ * so can be incremented directly.
+ */
+static inline void set_mm_counter(struct mm_struct *mm, int member, long value)
+{
+       mm->rss_stat.count[member] = value;
+}
+
+static inline unsigned long get_mm_counter(struct mm_struct *mm, int member)
+{
+       return mm->rss_stat.count[member];
+}
+
+static inline void add_mm_counter(struct mm_struct *mm, int member, long value)
+{
+       mm->rss_stat.count[member] += value;
+}
+
+static inline void inc_mm_counter(struct mm_struct *mm, int member)
+{
+       mm->rss_stat.count[member]++;
+}
+
+static inline void dec_mm_counter(struct mm_struct *mm, int member)
+{
+       mm->rss_stat.count[member]--;
+}
+
+#endif /* !USE_SPLIT_PTLOCKS */
+
+static inline unsigned long get_mm_rss(struct mm_struct *mm)
+{
+       return get_mm_counter(mm, MM_FILEPAGES) +
+               get_mm_counter(mm, MM_ANONPAGES);
+}
+
+static inline unsigned long get_mm_hiwater_rss(struct mm_struct *mm)
+{
+       return max(mm->hiwater_rss, get_mm_rss(mm));
+}
+
+static inline unsigned long get_mm_hiwater_vm(struct mm_struct *mm)
+{
+       return max(mm->hiwater_vm, mm->total_vm);
+}
+
+static inline void update_hiwater_rss(struct mm_struct *mm)
+{
+       unsigned long _rss = get_mm_rss(mm);
+
+       if ((mm)->hiwater_rss < _rss)
+               (mm)->hiwater_rss = _rss;
+}
+
+static inline void update_hiwater_vm(struct mm_struct *mm)
+{
+       if (mm->hiwater_vm < mm->total_vm)
+               mm->hiwater_vm = mm->total_vm;
+}
+
+static inline void setmax_mm_hiwater_rss(unsigned long *maxrss,
+                                        struct mm_struct *mm)
+{
+       unsigned long hiwater_rss = get_mm_hiwater_rss(mm);
+
+       if (*maxrss < hiwater_rss)
+               *maxrss = hiwater_rss;
+}
+
+void sync_mm_rss(struct task_struct *task, struct mm_struct *mm);
 
 /*
  * A callback you can register to apply pressure to ageable caches.
@@ -1114,7 +1216,7 @@ static inline void vma_nonlinear_insert(struct vm_area_struct *vma,
 
 /* mmap.c */
 extern int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin);
-extern void vma_adjust(struct vm_area_struct *vma, unsigned long start,
+extern int vma_adjust(struct vm_area_struct *vma, unsigned long start,
        unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert);
 extern struct vm_area_struct *vma_merge(struct mm_struct *,
        struct vm_area_struct *prev, unsigned long addr, unsigned long end,
index 36f96271306c728f17ebfc23f538acd092ed2dac..048b46270aa5220c8c70b1cf2637b1c8402bed32 100644 (file)
@@ -24,12 +24,6 @@ struct address_space;
 
 #define USE_SPLIT_PTLOCKS      (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)
 
-#if USE_SPLIT_PTLOCKS
-typedef atomic_long_t mm_counter_t;
-#else  /* !USE_SPLIT_PTLOCKS */
-typedef unsigned long mm_counter_t;
-#endif /* !USE_SPLIT_PTLOCKS */
-
 /*
  * Each physical page in the system has a struct page associated with
  * it to keep track of whatever it is we are using the page for at the
@@ -169,7 +163,8 @@ struct vm_area_struct {
         * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
         * or brk vma (with NULL file) can only be in an anon_vma list.
         */
-       struct list_head anon_vma_node; /* Serialized by anon_vma->lock */
+       struct list_head anon_vma_chain; /* Serialized by mmap_sem &
+                                         * page_table_lock */
        struct anon_vma *anon_vma;      /* Serialized by page_table_lock */
 
        /* Function pointers to deal with this struct. */
@@ -201,6 +196,29 @@ struct core_state {
        struct completion startup;
 };
 
+enum {
+       MM_FILEPAGES,
+       MM_ANONPAGES,
+       MM_SWAPENTS,
+       NR_MM_COUNTERS
+};
+
+#if USE_SPLIT_PTLOCKS
+#define SPLIT_RSS_COUNTING
+struct mm_rss_stat {
+       atomic_long_t count[NR_MM_COUNTERS];
+};
+/* per-thread cached information, */
+struct task_rss_stat {
+       int events;     /* for synchronization threshold */
+       int count[NR_MM_COUNTERS];
+};
+#else  /* !USE_SPLIT_PTLOCKS */
+struct mm_rss_stat {
+       unsigned long count[NR_MM_COUNTERS];
+};
+#endif /* !USE_SPLIT_PTLOCKS */
+
 struct mm_struct {
        struct vm_area_struct * mmap;           /* list of VMAs */
        struct rb_root mm_rb;
@@ -227,11 +245,6 @@ struct mm_struct {
                                                 * by mmlist_lock
                                                 */
 
-       /* Special counters, in some configurations protected by the
-        * page_table_lock, in other configurations by being atomic.
-        */
-       mm_counter_t _file_rss;
-       mm_counter_t _anon_rss;
 
        unsigned long hiwater_rss;      /* High-watermark of RSS usage */
        unsigned long hiwater_vm;       /* High-water virtual memory usage */
@@ -244,6 +257,12 @@ struct mm_struct {
 
        unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
 
+       /*
+        * Special counters, in some configurations protected by the
+        * page_table_lock, in other configurations by being atomic.
+        */
+       struct mm_rss_stat rss_stat;
+
        struct linux_binfmt *binfmt;
 
        cpumask_t cpu_vm_mask;
index 2ee22e8af11042d9ab266b30300b9a63f9e7d61a..d02d2c6e0cfe474253a3c183d59219efa3b3af47 100644 (file)
@@ -99,6 +99,8 @@ struct mmc_card {
 #define MMC_STATE_BLOCKADDR    (1<<3)          /* card uses block-addressing */
        unsigned int            quirks;         /* card quirks */
 #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes outside of the VS CCCR range */
+#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize */
+                                               /* for byte mode */
 
        u32                     raw_cid[4];     /* raw card CID */
        u32                     raw_csd[4];     /* raw card CSD */
@@ -139,6 +141,11 @@ static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
        return c->quirks & MMC_QUIRK_LENIENT_FN0;
 }
 
+static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c)
+{
+       return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+}
+
 #define mmc_card_name(c)       ((c)->cid.prod_name)
 #define mmc_card_id(c)         (dev_name(&(c)->dev))
 
index eaf36364b7d44ef778c1b8f346cc09481c6ae53f..43eaf5ca58481a732a96e80507ead6246ed3d9c2 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/sched.h>
 
 #include <linux/mmc/core.h>
+#include <linux/mmc/pm.h>
 
 struct mmc_ios {
        unsigned int    clock;                  /* clock rate */
@@ -152,6 +153,8 @@ struct mmc_host {
 #define MMC_CAP_NONREMOVABLE   (1 << 8)        /* Nonremovable e.g. eMMC */
 #define MMC_CAP_WAIT_WHILE_BUSY        (1 << 9)        /* Waits while card is busy */
 
+       mmc_pm_flag_t           pm_caps;        /* supported pm features */
+
        /* host specific block data */
        unsigned int            max_seg_size;   /* see blk_queue_max_segment_size */
        unsigned short          max_hw_segs;    /* see blk_queue_max_hw_segments */
@@ -197,6 +200,8 @@ struct mmc_host {
        struct task_struct      *sdio_irq_thread;
        atomic_t                sdio_irq_thread_abort;
 
+       mmc_pm_flag_t           pm_flags;       /* requested pm features */
+
 #ifdef CONFIG_LEDS_TRIGGERS
        struct led_trigger      *led;           /* activity led */
 #endif
diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h
new file mode 100644 (file)
index 0000000..d37aac4
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * linux/include/linux/mmc/pm.h
+ *
+ * Author:     Nicolas Pitre
+ * Copyright:  (C) 2009 Marvell Technology Group Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef LINUX_MMC_PM_H
+#define LINUX_MMC_PM_H
+
+/*
+ * These flags are used to describe power management features that
+ * some cards (typically SDIO cards) might wish to benefit from when
+ * the host system is being suspended.  There are several layers of
+ * abstractions involved, from the host controller driver, to the MMC core
+ * code, to the SDIO core code, to finally get to the actual SDIO function
+ * driver.  This file is therefore used for common definitions shared across
+ * all those layers.
+ */
+
+typedef unsigned int mmc_pm_flag_t;
+
+#define MMC_PM_KEEP_POWER      (1 << 0)        /* preserve card power during suspend */
+#define MMC_PM_WAKE_SDIO_IRQ   (1 << 1)        /* wake up host system on SDIO IRQ assertion */
+
+#endif
index 47ba464f5170eb3695d11192b28ee96601ead6b8..0ebaef577ff59ef0666e68357f4e61fa08f2a864 100644 (file)
@@ -95,6 +95,8 @@
 #define  SDIO_BUS_WIDTH_1BIT   0x00
 #define  SDIO_BUS_WIDTH_4BIT   0x02
 
+#define  SDIO_BUS_ASYNC_INT    0x20
+
 #define  SDIO_BUS_CD_DISABLE     0x80  /* disable pull-up on DAT3 (pin 1) */
 
 #define SDIO_CCCR_CAPS         0x08
index ac3ab683fec6707fec51881eb300fbe697239a3f..c6c0cceba5fe735a0719cdfc615a6cf89171b186 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 
+#include <linux/mmc/pm.h>
+
 struct mmc_card;
 struct sdio_func;
 
@@ -153,5 +155,8 @@ extern unsigned char sdio_f0_readb(struct sdio_func *func,
 extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
        unsigned int addr, int *err_ret);
 
+extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func);
+extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags);
+
 #endif
 
index a01a103341bd9d5e89a27d0366e99b5faf8d6ac8..bc209d8b7b5cf8772fd1dea25c0e0d2570b01548 100644 (file)
@@ -306,6 +306,7 @@ struct zone {
         * free areas of different sizes
         */
        spinlock_t              lock;
+       int                     all_unreclaimable; /* All pages pinned */
 #ifdef CONFIG_MEMORY_HOTPLUG
        /* see spanned/present_pages for more description */
        seqlock_t               span_seqlock;
@@ -417,7 +418,6 @@ struct zone {
 } ____cacheline_internodealigned_in_smp;
 
 typedef enum {
-       ZONE_ALL_UNRECLAIMABLE,         /* all pages pinned */
        ZONE_RECLAIM_LOCKED,            /* prevents concurrent reclaim */
        ZONE_OOM_LOCKED,                /* zone is in OOM killer zonelist */
 } zone_flags_t;
@@ -437,11 +437,6 @@ static inline void zone_clear_flag(struct zone *zone, zone_flags_t flag)
        clear_bit(flag, &zone->flags);
 }
 
-static inline int zone_is_all_unreclaimable(const struct zone *zone)
-{
-       return test_bit(ZONE_ALL_UNRECLAIMABLE, &zone->flags);
-}
-
 static inline int zone_is_reclaim_locked(const struct zone *zone)
 {
        return test_bit(ZONE_RECLAIM_LOCKED, &zone->flags);
index d74785c2393ad03976925e8ea6b63e3716718b79..0b89efc6f2155c1844775688c261d9ce52dc7957 100644 (file)
@@ -35,6 +35,7 @@ static inline void get_mnt_ns(struct mnt_namespace *ns)
 extern const struct seq_operations mounts_op;
 extern const struct seq_operations mountinfo_op;
 extern const struct seq_operations mountstats_op;
+extern int mnt_had_events(struct proc_mounts *);
 
 #endif
 #endif
index b5f43a34ef8841b9a2a5afe7fd961b85ad988d8b..4bd05474d11d557cd709ab3aa045902a8014af7c 100644 (file)
@@ -34,7 +34,18 @@ struct mnt_namespace;
 
 #define MNT_SHARED     0x1000  /* if the vfsmount is a shared mount */
 #define MNT_UNBINDABLE 0x2000  /* if the vfsmount is a unbindable mount */
-#define MNT_PNODE_MASK 0x3000  /* propagation flag mask */
+/*
+ * MNT_SHARED_MASK is the set of flags that should be cleared when a
+ * mount becomes shared.  Currently, this is only the flag that says a
+ * mount cannot be bind mounted, since this is how we create a mount
+ * that shares events with another mount.  If you add a new MNT_*
+ * flag, consider how it interacts with shared mounts.
+ */
+#define MNT_SHARED_MASK        (MNT_UNBINDABLE)
+#define MNT_PROPAGATION_MASK   (MNT_SHARED | MNT_UNBINDABLE)
+
+
+#define MNT_INTERNAL   0x4000
 
 struct vfsmount {
        struct list_head mnt_hash;
@@ -123,7 +134,6 @@ extern int do_add_mount(struct vfsmount *newmnt, struct path *path,
 
 extern void mark_mounts_for_expiry(struct list_head *mounts);
 
-extern spinlock_t vfsmount_lock;
 extern dev_t name_to_dev_t(char *name);
 
 #endif /* _LINUX_MOUNT_H */
index d09db1bc90830af34e6985129b52109d8b5fa05f..1a0b85aa151e313e06912895c42a25a2b0c3c55d 100644 (file)
@@ -33,9 +33,6 @@
 #define FLUSH_STABLE           4       /* commit to stable storage */
 #define FLUSH_LOWPRI           8       /* low priority background flush */
 #define FLUSH_HIGHPRI          16      /* high priority memory reclaim flush */
-#define FLUSH_NOCOMMIT         32      /* Don't send the NFSv3/v4 COMMIT */
-#define FLUSH_INVALIDATE       64      /* Invalidate the page cache */
-#define FLUSH_NOWRITEPAGE      128     /* Don't call writepage() */
 
 #ifdef __KERNEL__
 
@@ -166,6 +163,7 @@ struct nfs_inode {
        struct radix_tree_root  nfs_page_tree;
 
        unsigned long           npages;
+       unsigned long           ncommit;
 
        /* Open contexts for shared mmap writes */
        struct list_head        open_files;
@@ -349,7 +347,6 @@ extern int nfs_attribute_timeout(struct inode *inode);
 extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
 extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
 extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
-extern int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping);
 extern int nfs_setattr(struct dentry *, struct iattr *);
 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
 extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
@@ -477,21 +474,12 @@ extern int nfs_writeback_done(struct rpc_task *, struct nfs_write_data *);
  * Try to write back everything synchronously (but check the
  * return value!)
  */
-extern long nfs_sync_mapping_wait(struct address_space *, struct writeback_control *, int);
 extern int nfs_wb_all(struct inode *inode);
-extern int nfs_wb_nocommit(struct inode *inode);
 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);
-#else
-static inline int
-nfs_commit_inode(struct inode *inode, int how)
-{
-       return 0;
-}
 #endif
 
 static inline int
index 6a2e44fd75e23ed0b72be0498decb3de28a84183..717a5e54eb1dcf68cdda886b71601e003fec105a 100644 (file)
@@ -193,6 +193,8 @@ struct nfs4_slot_table {
        int             max_slots;              /* # slots in table */
        int             highest_used_slotid;    /* sent to server on each SEQ.
                                                 * op for dynamic resizing */
+       int             target_max_slots;       /* Set by CB_RECALL_SLOT as
+                                                * the new max_slots */
 };
 
 static inline int slot_idx(struct nfs4_slot_table *tbl, struct nfs4_slot *sp)
index 454997cccbd8c795aa0210dbd50bc0a7606e06d1..c4fa64b585ffa8469ff18a993f37a4edf1faa45a 100644 (file)
@@ -69,8 +69,6 @@
  * int node_online(node)               Is some node online?
  * int node_possible(node)             Is some node possible?
  *
- * int any_online_node(mask)           First online node in mask
- *
  * node_set_online(node)               set bit 'node' in node_online_map
  * node_set_offline(node)              clear bit 'node' in node_online_map
  *
@@ -467,15 +465,6 @@ static inline int num_node_state(enum node_states state)
 #define node_online_map        node_states[N_ONLINE]
 #define node_possible_map      node_states[N_POSSIBLE]
 
-#define any_online_node(mask)                  \
-({                                             \
-       int node;                               \
-       for_each_node_mask(node, (mask))        \
-               if (node_online(node))          \
-                       break;                  \
-       node;                                   \
-})
-
 #define num_online_nodes()     num_node_state(N_ONLINE)
 #define num_possible_nodes()   num_node_state(N_POSSIBLE)
 #define node_online(node)      node_state((node), N_ONLINE)
index e80df06ad22ab1aaa7d86a76434a4a5b9a2aadf8..8e258c727971227b365b49d3a49e9e4efedccbcc 100644 (file)
@@ -215,20 +215,59 @@ struct dev_pm_ops {
        int (*runtime_idle)(struct device *dev);
 };
 
+#ifdef CONFIG_PM_SLEEP
+#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
+       .suspend = suspend_fn, \
+       .resume = resume_fn, \
+       .freeze = suspend_fn, \
+       .thaw = resume_fn, \
+       .poweroff = suspend_fn, \
+       .restore = resume_fn,
+#else
+#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
+       .runtime_suspend = suspend_fn, \
+       .runtime_resume = resume_fn, \
+       .runtime_idle = idle_fn,
+#else
+#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)
+#endif
+
 /*
  * Use this if you want to use the same suspend and resume callbacks for suspend
  * to RAM and hibernation.
  */
 #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
 const struct dev_pm_ops name = { \
-       .suspend = suspend_fn, \
-       .resume = resume_fn, \
-       .freeze = suspend_fn, \
-       .thaw = resume_fn, \
-       .poweroff = suspend_fn, \
-       .restore = resume_fn, \
+       SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
+}
+
+/*
+ * Use this for defining a set of PM operations to be used in all situations
+ * (sustem suspend, hibernation or runtime PM).
+ */
+#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \
+const struct dev_pm_ops name = { \
+       SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
+       SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
 }
 
+/*
+ * Use this for subsystems (bus types, device types, device classes) that don't
+ * need any special suspend/resume handling in addition to invoking the PM
+ * callbacks provided by device drivers supporting both the system sleep PM and
+ * runtime PM, make the pm member point to generic_subsys_pm_ops.
+ */
+#ifdef CONFIG_PM_OPS
+extern struct dev_pm_ops generic_subsys_pm_ops;
+#define GENERIC_SUBSYS_PM_OPS  (&generic_subsys_pm_ops)
+#else
+#define GENERIC_SUBSYS_PM_OPS  NULL
+#endif
+
 /**
  * PM_EVENT_ messages
  *
index 7d773aac5314765f131fd5f108a55f800ee3e589..b776db737244a114a8cbd257a255404b15e53155 100644 (file)
@@ -62,6 +62,11 @@ static inline void device_set_run_wake(struct device *dev, bool enable)
        dev->power.run_wake = enable;
 }
 
+static inline bool pm_runtime_suspended(struct device *dev)
+{
+       return dev->power.runtime_status == RPM_SUSPENDED;
+}
+
 #else /* !CONFIG_PM_RUNTIME */
 
 static inline int pm_runtime_idle(struct device *dev) { return -ENOSYS; }
@@ -89,6 +94,7 @@ static inline void pm_runtime_get_noresume(struct device *dev) {}
 static inline void pm_runtime_put_noidle(struct device *dev) {}
 static inline bool device_run_wake(struct device *dev) { return false; }
 static inline void device_set_run_wake(struct device *dev, bool enable) {}
+static inline bool pm_runtime_suspended(struct device *dev) { return false; }
 
 #endif /* !CONFIG_PM_RUNTIME */
 
index a6861f11748044d2c666c6f94edf463c8c47660a..b462916b2a0a4c53d8af6e1c14f4f94491907e2a 100644 (file)
@@ -279,9 +279,6 @@ struct dquot {
        struct mem_dqblk dq_dqb;        /* Diskquota usage */
 };
 
-#define QUOTA_OK          0
-#define NO_QUOTA          1
-
 /* Operations which must be implemented by each quota format */
 struct quota_format_ops {
        int (*check_quota_file)(struct super_block *sb, int type);      /* Detect whether file is in our format */
@@ -295,13 +292,6 @@ struct quota_format_ops {
 
 /* Operations working with dquots */
 struct dquot_operations {
-       int (*initialize) (struct inode *, int);
-       int (*drop) (struct inode *);
-       int (*alloc_space) (struct inode *, qsize_t, int);
-       int (*alloc_inode) (const struct inode *, qsize_t);
-       int (*free_space) (struct inode *, qsize_t);
-       int (*free_inode) (const struct inode *, qsize_t);
-       int (*transfer) (struct inode *, struct iattr *);
        int (*write_dquot) (struct dquot *);            /* Ordinary dquot write */
        struct dquot *(*alloc_dquot)(struct super_block *, int);        /* Allocate memory for new dquot */
        void (*destroy_dquot)(struct dquot *);          /* Free memory for dquot */
@@ -309,12 +299,6 @@ struct dquot_operations {
        int (*release_dquot) (struct dquot *);          /* Quota is going to be deleted from disk */
        int (*mark_dirty) (struct dquot *);             /* Dquot is marked dirty */
        int (*write_info) (struct super_block *, int);  /* Write of quota "superblock" */
-       /* reserve quota for delayed block allocation */
-       int (*reserve_space) (struct inode *, qsize_t, int);
-       /* claim reserved quota for delayed alloc */
-       int (*claim_space) (struct inode *, qsize_t);
-       /* release rsved quota for delayed alloc */
-       void (*release_rsv) (struct inode *, qsize_t);
        /* get reserved quota for delayed alloc, value returned is managed by
         * quota code only */
        qsize_t *(*get_reserved_space) (struct inode *);
@@ -324,7 +308,7 @@ struct dquot_operations {
 struct quotactl_ops {
        int (*quota_on)(struct super_block *, int, int, char *, int);
        int (*quota_off)(struct super_block *, int, int);
-       int (*quota_sync)(struct super_block *, int);
+       int (*quota_sync)(struct super_block *, int, int);
        int (*get_info)(struct super_block *, int, struct if_dqinfo *);
        int (*set_info)(struct super_block *, int, struct if_dqinfo *);
        int (*get_dqblk)(struct super_block *, int, qid_t, struct if_dqblk *);
@@ -357,26 +341,25 @@ enum {
 #define DQUOT_STATE_FLAGS      (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \
                                 DQUOT_SUSPENDED)
 /* Other quota flags */
-#define DQUOT_QUOTA_SYS_FILE   (1 << 6)        /* Quota file is a special
+#define DQUOT_STATE_LAST       (_DQUOT_STATE_FLAGS * MAXQUOTAS)
+#define DQUOT_QUOTA_SYS_FILE   (1 << DQUOT_STATE_LAST)
+                                               /* Quota file is a special
                                                 * system file and user cannot
                                                 * touch it. Filesystem is
                                                 * responsible for setting
                                                 * S_NOQUOTA, S_NOATIME flags
                                                 */
-#define DQUOT_NEGATIVE_USAGE   (1 << 7)        /* Allow negative quota usage */
+#define DQUOT_NEGATIVE_USAGE   (1 << (DQUOT_STATE_LAST + 1))
+                                              /* Allow negative quota usage */
 
 static inline unsigned int dquot_state_flag(unsigned int flags, int type)
 {
-       if (type == USRQUOTA)
-               return flags;
-       return flags << _DQUOT_STATE_FLAGS;
+       return flags << _DQUOT_STATE_FLAGS * type;
 }
 
 static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
 {
-       if (type == USRQUOTA)
-               return flags;
-       return flags >> _DQUOT_STATE_FLAGS;
+       return (flags >> _DQUOT_STATE_FLAGS * type) & DQUOT_STATE_FLAGS;
 }
 
 #ifdef CONFIG_QUOTA_NETLINK_INTERFACE
index 3ebb231536405d5c6e557f337692de98969d8c63..e6fa7acce2900a5bd6862a4bf9703a87cd30a0bd 100644 (file)
@@ -19,15 +19,12 @@ static inline struct quota_info *sb_dqopt(struct super_block *sb)
 /*
  * declaration of quota_function calls in kernel.
  */
-void sync_quota_sb(struct super_block *sb, int type);
-static inline void writeout_quota_sb(struct super_block *sb, int type)
-{
-       if (sb->s_qcop->quota_sync)
-               sb->s_qcop->quota_sync(sb, type);
-}
+void inode_add_rsv_space(struct inode *inode, qsize_t number);
+void inode_claim_rsv_space(struct inode *inode, qsize_t number);
+void inode_sub_rsv_space(struct inode *inode, qsize_t number);
 
-int dquot_initialize(struct inode *inode, int type);
-int dquot_drop(struct inode *inode);
+void dquot_initialize(struct inode *inode);
+void dquot_drop(struct inode *inode);
 struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
 void dqput(struct dquot *dquot);
 int dquot_scan_active(struct super_block *sb,
@@ -36,24 +33,23 @@ int dquot_scan_active(struct super_block *sb,
 struct dquot *dquot_alloc(struct super_block *sb, int type);
 void dquot_destroy(struct dquot *dquot);
 
-int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc);
-int dquot_alloc_inode(const struct inode *inode, qsize_t number);
+int __dquot_alloc_space(struct inode *inode, qsize_t number,
+               int warn, int reserve);
+void __dquot_free_space(struct inode *inode, qsize_t number, int reserve);
 
-int dquot_reserve_space(struct inode *inode, qsize_t number, int prealloc);
-int dquot_claim_space(struct inode *inode, qsize_t number);
-void dquot_release_reserved_space(struct inode *inode, qsize_t number);
-qsize_t dquot_get_reserved_space(struct inode *inode);
+int dquot_alloc_inode(const struct inode *inode);
 
-int dquot_free_space(struct inode *inode, qsize_t number);
-int dquot_free_inode(const struct inode *inode, qsize_t number);
+int dquot_claim_space_nodirty(struct inode *inode, qsize_t number);
+void dquot_free_inode(const struct inode *inode);
 
-int dquot_transfer(struct inode *inode, struct iattr *iattr);
 int dquot_commit(struct dquot *dquot);
 int dquot_acquire(struct dquot *dquot);
 int dquot_release(struct dquot *dquot);
 int dquot_commit_info(struct super_block *sb, int type);
 int dquot_mark_dquot_dirty(struct dquot *dquot);
 
+int dquot_file_open(struct inode *inode, struct file *file);
+
 int vfs_quota_on(struct super_block *sb, int type, int format_id,
        char *path, int remount);
 int vfs_quota_enable(struct inode *inode, int type, int format_id,
@@ -64,14 +60,13 @@ int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
        int format_id, int type);
 int vfs_quota_off(struct super_block *sb, int type, int remount);
 int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags);
-int vfs_quota_sync(struct super_block *sb, int type);
+int vfs_quota_sync(struct super_block *sb, int type, int wait);
 int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
 int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
 int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di);
 int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di);
 
-void vfs_dq_drop(struct inode *inode);
-int vfs_dq_transfer(struct inode *inode, struct iattr *iattr);
+int dquot_transfer(struct inode *inode, struct iattr *iattr);
 int vfs_dq_quota_on_remount(struct super_block *sb);
 
 static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
@@ -83,53 +78,56 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
  * Functions for checking status of quota
  */
 
-static inline int sb_has_quota_usage_enabled(struct super_block *sb, int type)
+static inline bool sb_has_quota_usage_enabled(struct super_block *sb, int type)
 {
        return sb_dqopt(sb)->flags &
                                dquot_state_flag(DQUOT_USAGE_ENABLED, type);
 }
 
-static inline int sb_has_quota_limits_enabled(struct super_block *sb, int type)
+static inline bool sb_has_quota_limits_enabled(struct super_block *sb, int type)
 {
        return sb_dqopt(sb)->flags &
                                dquot_state_flag(DQUOT_LIMITS_ENABLED, type);
 }
 
-static inline int sb_has_quota_suspended(struct super_block *sb, int type)
+static inline bool sb_has_quota_suspended(struct super_block *sb, int type)
 {
        return sb_dqopt(sb)->flags &
                                dquot_state_flag(DQUOT_SUSPENDED, type);
 }
 
-static inline int sb_any_quota_suspended(struct super_block *sb)
+static inline unsigned sb_any_quota_suspended(struct super_block *sb)
 {
-       return sb_has_quota_suspended(sb, USRQUOTA) ||
-               sb_has_quota_suspended(sb, GRPQUOTA);
+       unsigned type, tmsk = 0;
+       for (type = 0; type < MAXQUOTAS; type++)
+               tmsk |= sb_has_quota_suspended(sb, type) << type;
+       return tmsk;
 }
 
 /* Does kernel know about any quota information for given sb + type? */
-static inline int sb_has_quota_loaded(struct super_block *sb, int type)
+static inline bool sb_has_quota_loaded(struct super_block *sb, int type)
 {
        /* Currently if anything is on, then quota usage is on as well */
        return sb_has_quota_usage_enabled(sb, type);
 }
 
-static inline int sb_any_quota_loaded(struct super_block *sb)
+static inline unsigned sb_any_quota_loaded(struct super_block *sb)
 {
-       return sb_has_quota_loaded(sb, USRQUOTA) ||
-               sb_has_quota_loaded(sb, GRPQUOTA);
+       unsigned type, tmsk = 0;
+       for (type = 0; type < MAXQUOTAS; type++)
+               tmsk |= sb_has_quota_loaded(sb, type) << type;
+       return  tmsk;
 }
 
-static inline int sb_has_quota_active(struct super_block *sb, int type)
+static inline bool sb_has_quota_active(struct super_block *sb, int type)
 {
        return sb_has_quota_loaded(sb, type) &&
               !sb_has_quota_suspended(sb, type);
 }
 
-static inline int sb_any_quota_active(struct super_block *sb)
+static inline unsigned sb_any_quota_active(struct super_block *sb)
 {
-       return sb_has_quota_active(sb, USRQUOTA) ||
-              sb_has_quota_active(sb, GRPQUOTA);
+       return sb_any_quota_loaded(sb) & ~sb_any_quota_suspended(sb);
 }
 
 /*
@@ -141,122 +139,6 @@ extern const struct quotactl_ops vfs_quotactl_ops;
 #define sb_dquot_ops (&dquot_operations)
 #define sb_quotactl_ops (&vfs_quotactl_ops)
 
-/* It is better to call this function outside of any transaction as it might
- * need a lot of space in journal for dquot structure allocation. */
-static inline void vfs_dq_init(struct inode *inode)
-{
-       BUG_ON(!inode->i_sb);
-       if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode))
-               inode->i_sb->dq_op->initialize(inode, -1);
-}
-
-/* The following allocation/freeing/transfer functions *must* be called inside
- * a transaction (deadlocks possible otherwise) */
-static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr)
-{
-       if (sb_any_quota_active(inode->i_sb)) {
-               /* Used space is updated in alloc_space() */
-               if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA)
-                       return 1;
-       }
-       else
-               inode_add_bytes(inode, nr);
-       return 0;
-}
-
-static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr)
-{
-       int ret;
-        if (!(ret =  vfs_dq_prealloc_space_nodirty(inode, nr)))
-               mark_inode_dirty(inode);
-       return ret;
-}
-
-static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr)
-{
-       if (sb_any_quota_active(inode->i_sb)) {
-               /* Used space is updated in alloc_space() */
-               if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA)
-                       return 1;
-       }
-       else
-               inode_add_bytes(inode, nr);
-       return 0;
-}
-
-static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr)
-{
-       int ret;
-       if (!(ret = vfs_dq_alloc_space_nodirty(inode, nr)))
-               mark_inode_dirty(inode);
-       return ret;
-}
-
-static inline int vfs_dq_reserve_space(struct inode *inode, qsize_t nr)
-{
-       if (sb_any_quota_active(inode->i_sb)) {
-               /* Used space is updated in alloc_space() */
-               if (inode->i_sb->dq_op->reserve_space(inode, nr, 0) == NO_QUOTA)
-                       return 1;
-       }
-       return 0;
-}
-
-static inline int vfs_dq_alloc_inode(struct inode *inode)
-{
-       if (sb_any_quota_active(inode->i_sb)) {
-               vfs_dq_init(inode);
-               if (inode->i_sb->dq_op->alloc_inode(inode, 1) == NO_QUOTA)
-                       return 1;
-       }
-       return 0;
-}
-
-/*
- * Convert in-memory reserved quotas to real consumed quotas
- */
-static inline int vfs_dq_claim_space(struct inode *inode, qsize_t nr)
-{
-       if (sb_any_quota_active(inode->i_sb)) {
-               if (inode->i_sb->dq_op->claim_space(inode, nr) == NO_QUOTA)
-                       return 1;
-       } else
-               inode_add_bytes(inode, nr);
-
-       mark_inode_dirty(inode);
-       return 0;
-}
-
-/*
- * Release reserved (in-memory) quotas
- */
-static inline
-void vfs_dq_release_reservation_space(struct inode *inode, qsize_t nr)
-{
-       if (sb_any_quota_active(inode->i_sb))
-               inode->i_sb->dq_op->release_rsv(inode, nr);
-}
-
-static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
-{
-       if (sb_any_quota_active(inode->i_sb))
-               inode->i_sb->dq_op->free_space(inode, nr);
-       else
-               inode_sub_bytes(inode, nr);
-}
-
-static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr)
-{
-       vfs_dq_free_space_nodirty(inode, nr);
-       mark_inode_dirty(inode);
-}
-
-static inline void vfs_dq_free_inode(struct inode *inode)
-{
-       if (sb_any_quota_active(inode->i_sb))
-               inode->i_sb->dq_op->free_inode(inode, 1);
-}
-
 /* Cannot be called inside a transaction */
 static inline int vfs_dq_off(struct super_block *sb, int remount)
 {
@@ -316,28 +198,20 @@ static inline int sb_any_quota_active(struct super_block *sb)
 #define sb_dquot_ops                           (NULL)
 #define sb_quotactl_ops                                (NULL)
 
-static inline void vfs_dq_init(struct inode *inode)
+static inline void dquot_initialize(struct inode *inode)
 {
 }
 
-static inline void vfs_dq_drop(struct inode *inode)
+static inline void dquot_drop(struct inode *inode)
 {
 }
 
-static inline int vfs_dq_alloc_inode(struct inode *inode)
+static inline int dquot_alloc_inode(const struct inode *inode)
 {
        return 0;
 }
 
-static inline void vfs_dq_free_inode(struct inode *inode)
-{
-}
-
-static inline void sync_quota_sb(struct super_block *sb, int type)
-{
-}
-
-static inline void writeout_quota_sb(struct super_block *sb, int type)
+static inline void dquot_free_inode(const struct inode *inode)
 {
 }
 
@@ -351,110 +225,116 @@ static inline int vfs_dq_quota_on_remount(struct super_block *sb)
        return 0;
 }
 
-static inline int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
+static inline int dquot_transfer(struct inode *inode, struct iattr *iattr)
 {
        return 0;
 }
 
-static inline int vfs_dq_prealloc_space_nodirty(struct inode *inode, qsize_t nr)
+static inline int __dquot_alloc_space(struct inode *inode, qsize_t number,
+               int warn, int reserve)
 {
-       inode_add_bytes(inode, nr);
+       if (!reserve)
+               inode_add_bytes(inode, number);
        return 0;
 }
 
-static inline int vfs_dq_prealloc_space(struct inode *inode, qsize_t nr)
+static inline void __dquot_free_space(struct inode *inode, qsize_t number,
+               int reserve)
 {
-       vfs_dq_prealloc_space_nodirty(inode, nr);
-       mark_inode_dirty(inode);
-       return 0;
+       if (!reserve)
+               inode_sub_bytes(inode, number);
 }
 
-static inline int vfs_dq_alloc_space_nodirty(struct inode *inode, qsize_t nr)
+static inline int dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
 {
-       inode_add_bytes(inode, nr);
+       inode_add_bytes(inode, number);
        return 0;
 }
 
-static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr)
+#define dquot_file_open                generic_file_open
+
+#endif /* CONFIG_QUOTA */
+
+static inline int dquot_alloc_space_nodirty(struct inode *inode, qsize_t nr)
 {
-       vfs_dq_alloc_space_nodirty(inode, nr);
-       mark_inode_dirty(inode);
-       return 0;
+       return __dquot_alloc_space(inode, nr, 1, 0);
 }
 
-static inline int vfs_dq_reserve_space(struct inode *inode, qsize_t nr)
+static inline int dquot_alloc_space(struct inode *inode, qsize_t nr)
 {
-       return 0;
+       int ret;
+
+       ret = dquot_alloc_space_nodirty(inode, nr);
+       if (!ret)
+               mark_inode_dirty(inode);
+       return ret;
 }
 
-static inline int vfs_dq_claim_space(struct inode *inode, qsize_t nr)
+static inline int dquot_alloc_block_nodirty(struct inode *inode, qsize_t nr)
 {
-       return vfs_dq_alloc_space(inode, nr);
+       return dquot_alloc_space_nodirty(inode, nr << inode->i_blkbits);
 }
 
-static inline
-int vfs_dq_release_reservation_space(struct inode *inode, qsize_t nr)
+static inline int dquot_alloc_block(struct inode *inode, qsize_t nr)
 {
-       return 0;
+       return dquot_alloc_space(inode, nr << inode->i_blkbits);
 }
 
-static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
+static inline int dquot_prealloc_block_nodirty(struct inode *inode, qsize_t nr)
 {
-       inode_sub_bytes(inode, nr);
+       return __dquot_alloc_space(inode, nr << inode->i_blkbits, 0, 0);
 }
 
-static inline void vfs_dq_free_space(struct inode *inode, qsize_t nr)
+static inline int dquot_prealloc_block(struct inode *inode, qsize_t nr)
 {
-       vfs_dq_free_space_nodirty(inode, nr);
-       mark_inode_dirty(inode);
-}      
-
-#endif /* CONFIG_QUOTA */
+       int ret;
 
-static inline int vfs_dq_prealloc_block_nodirty(struct inode *inode, qsize_t nr)
-{
-       return vfs_dq_prealloc_space_nodirty(inode, nr << inode->i_blkbits);
+       ret = dquot_prealloc_block_nodirty(inode, nr);
+       if (!ret)
+               mark_inode_dirty(inode);
+       return ret;
 }
 
-static inline int vfs_dq_prealloc_block(struct inode *inode, qsize_t nr)
+static inline int dquot_reserve_block(struct inode *inode, qsize_t nr)
 {
-       return vfs_dq_prealloc_space(inode, nr << inode->i_blkbits);
+       return __dquot_alloc_space(inode, nr << inode->i_blkbits, 1, 1);
 }
 
-static inline int vfs_dq_alloc_block_nodirty(struct inode *inode, qsize_t nr)
+static inline int dquot_claim_block(struct inode *inode, qsize_t nr)
 {
-       return vfs_dq_alloc_space_nodirty(inode, nr << inode->i_blkbits);
-}
+       int ret;
 
-static inline int vfs_dq_alloc_block(struct inode *inode, qsize_t nr)
-{
-       return vfs_dq_alloc_space(inode, nr << inode->i_blkbits);
+       ret = dquot_claim_space_nodirty(inode, nr << inode->i_blkbits);
+       if (!ret)
+               mark_inode_dirty(inode);
+       return ret;
 }
 
-static inline int vfs_dq_reserve_block(struct inode *inode, qsize_t nr)
+static inline void dquot_free_space_nodirty(struct inode *inode, qsize_t nr)
 {
-       return vfs_dq_reserve_space(inode, nr << inode->i_blkbits);
+       __dquot_free_space(inode, nr, 0);
 }
 
-static inline int vfs_dq_claim_block(struct inode *inode, qsize_t nr)
+static inline void dquot_free_space(struct inode *inode, qsize_t nr)
 {
-       return vfs_dq_claim_space(inode, nr << inode->i_blkbits);
+       dquot_free_space_nodirty(inode, nr);
+       mark_inode_dirty(inode);
 }
 
-static inline
-void vfs_dq_release_reservation_block(struct inode *inode, qsize_t nr)
+static inline void dquot_free_block_nodirty(struct inode *inode, qsize_t nr)
 {
-       vfs_dq_release_reservation_space(inode, nr << inode->i_blkbits);
+       dquot_free_space_nodirty(inode, nr << inode->i_blkbits);
 }
 
-static inline void vfs_dq_free_block_nodirty(struct inode *inode, qsize_t nr)
+static inline void dquot_free_block(struct inode *inode, qsize_t nr)
 {
-       vfs_dq_free_space_nodirty(inode, nr << inode->i_blkbits);
+       dquot_free_space(inode, nr << inode->i_blkbits);
 }
 
-static inline void vfs_dq_free_block(struct inode *inode, qsize_t nr)
+static inline void dquot_release_reservation_block(struct inode *inode,
+               qsize_t nr)
 {
-       vfs_dq_free_space(inode, nr << inode->i_blkbits);
+       __dquot_free_space(inode, nr << inode->i_blkbits, 1);
 }
 
 #endif /* _LINUX_QUOTAOPS_ */
index 030d92255c7a09b8fd6175f93e0a1862b96c21a5..28c9fd020d3992669435ed8767e522e2f48b76b1 100644 (file)
@@ -89,8 +89,9 @@
  * REGULATION_OUT Regulator output is out of regulation.
  * FAIL           Regulator output has failed.
  * OVER_TEMP      Regulator over temp.
- * FORCE_DISABLE  Regulator shut down by software.
+ * FORCE_DISABLE  Regulator forcibly shut down by software.
  * VOLTAGE_CHANGE Regulator voltage changed.
+ * DISABLE        Regulator was disabled.
  *
  * NOTE: These events can be OR'ed together when passed into handler.
  */
 #define REGULATOR_EVENT_OVER_TEMP              0x10
 #define REGULATOR_EVENT_FORCE_DISABLE          0x20
 #define REGULATOR_EVENT_VOLTAGE_CHANGE         0x40
+#define REGULATOR_EVENT_DISABLE                0x80
 
 struct regulator;
 
index 31f2055eae282ff3b585cc2215c94cf10765b326..592cd7c642c22781187ad5c10d4906cbc6a49b32 100644 (file)
@@ -58,6 +58,9 @@ enum regulator_status {
  * @get_optimum_mode: Get the most efficient operating mode for the regulator
  *                    when running with the specified parameters.
  *
+ * @enable_time: Time taken for the regulator voltage output voltage to
+ *               stabalise after being enabled, in microseconds.
+ *
  * @set_suspend_voltage: Set the voltage for the regulator when the system
  *                       is suspended.
  * @set_suspend_enable: Mark the regulator as enabled when the system is
@@ -93,6 +96,9 @@ struct regulator_ops {
        int (*set_mode) (struct regulator_dev *, unsigned int mode);
        unsigned int (*get_mode) (struct regulator_dev *);
 
+       /* Time taken to enable the regulator */
+       int (*enable_time) (struct regulator_dev *);
+
        /* report regulator status ... most other accessors report
         * control inputs, this reports results of combining inputs
         * from Linux (and other sources) with the actual load.
index e94a4a1c7c8a705d6f0887f9d82cd7cef57f723f..ffd7d508e726f7d191296dac71254372b591d348 100644 (file)
@@ -25,6 +25,7 @@ struct regulator_init_data;
  * @microvolts:                Output voltage of regulator
  * @gpio:              GPIO to use for enable control
  *                     set to -EINVAL if not used
+ * @startup_delay:     Start-up time in microseconds
  * @enable_high:       Polarity of enable GPIO
  *                     1 = Active high, 0 = Active low
  * @enabled_at_boot:   Whether regulator has been enabled at
@@ -41,6 +42,7 @@ struct fixed_voltage_config {
        const char *supply_name;
        int microvolts;
        int gpio;
+       unsigned startup_delay;
        unsigned enable_high:1;
        unsigned enabled_at_boot:1;
        struct regulator_init_data *init_data;
diff --git a/include/linux/regulator/max8649.h b/include/linux/regulator/max8649.h
new file mode 100644 (file)
index 0000000..417d14e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Interface of Maxim max8649
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ *      Haojian Zhuang <haojian.zhuang@marvell.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 __LINUX_REGULATOR_MAX8649_H
+#define        __LINUX_REGULATOR_MAX8649_H
+
+#include <linux/regulator/machine.h>
+
+enum {
+       MAX8649_EXTCLK_26MHZ = 0,
+       MAX8649_EXTCLK_13MHZ,
+       MAX8649_EXTCLK_19MHZ,   /* 19.2MHz */
+};
+
+enum {
+       MAX8649_RAMP_32MV = 0,
+       MAX8649_RAMP_16MV,
+       MAX8649_RAMP_8MV,
+       MAX8649_RAMP_4MV,
+       MAX8649_RAMP_2MV,
+       MAX8649_RAMP_1MV,
+       MAX8649_RAMP_0_5MV,
+       MAX8649_RAMP_0_25MV,
+};
+
+struct max8649_platform_data {
+       struct regulator_init_data *regulator;
+
+       unsigned        mode:2;         /* bit[1:0] = VID1,VID0 */
+       unsigned        extclk_freq:2;
+       unsigned        extclk:1;
+       unsigned        ramp_timing:3;
+       unsigned        ramp_down:1;
+};
+
+#endif /* __LINUX_REGULATOR_MAX8649_H */
index 1ba3cf6edfbb4d80f17f76411c19360a0ac978c6..3b603f4741861f177bda12ede2669ad40b26e51b 100644 (file)
@@ -2034,7 +2034,7 @@ void reiserfs_read_locked_inode(struct inode *inode,
 int reiserfs_find_actor(struct inode *inode, void *p);
 int reiserfs_init_locked_inode(struct inode *inode, void *p);
 void reiserfs_delete_inode(struct inode *inode);
-int reiserfs_write_inode(struct inode *inode, int);
+int reiserfs_write_inode(struct inode *inode, struct writeback_control *wbc);
 int reiserfs_get_block(struct inode *inode, sector_t block,
                       struct buffer_head *bh_result, int create);
 struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
index b019ae64e2ab1d356daf92aaa7e92547227246c7..d25bd224d3707532198b1012dc2f4f158ca01875 100644 (file)
@@ -37,7 +37,27 @@ struct anon_vma {
         * is serialized by a system wide lock only visible to
         * mm_take_all_locks() (mm_all_locks_mutex).
         */
-       struct list_head head;  /* List of private "related" vmas */
+       struct list_head head;  /* Chain of private "related" vmas */
+};
+
+/*
+ * The copy-on-write semantics of fork mean that an anon_vma
+ * can become associated with multiple processes. Furthermore,
+ * each child process will have its own anon_vma, where new
+ * pages for that process are instantiated.
+ *
+ * This structure allows us to find the anon_vmas associated
+ * with a VMA, or the VMAs associated with an anon_vma.
+ * The "same_vma" list contains the anon_vma_chains linking
+ * all the anon_vmas associated with this VMA.
+ * The "same_anon_vma" list contains the anon_vma_chains
+ * which link all the VMAs associated with this anon_vma.
+ */
+struct anon_vma_chain {
+       struct vm_area_struct *vma;
+       struct anon_vma *anon_vma;
+       struct list_head same_vma;   /* locked by mmap_sem & page_table_lock */
+       struct list_head same_anon_vma; /* locked by anon_vma->lock */
 };
 
 #ifdef CONFIG_MMU
@@ -89,15 +109,23 @@ static inline void anon_vma_unlock(struct vm_area_struct *vma)
  */
 void anon_vma_init(void);      /* create anon_vma_cachep */
 int  anon_vma_prepare(struct vm_area_struct *);
-void __anon_vma_merge(struct vm_area_struct *, struct vm_area_struct *);
-void anon_vma_unlink(struct vm_area_struct *);
-void anon_vma_link(struct vm_area_struct *);
+void unlink_anon_vmas(struct vm_area_struct *);
+int anon_vma_clone(struct vm_area_struct *, struct vm_area_struct *);
+int anon_vma_fork(struct vm_area_struct *, struct vm_area_struct *);
 void __anon_vma_link(struct vm_area_struct *);
 void anon_vma_free(struct anon_vma *);
 
+static inline void anon_vma_merge(struct vm_area_struct *vma,
+                                 struct vm_area_struct *next)
+{
+       VM_BUG_ON(vma->anon_vma != next->anon_vma);
+       unlink_anon_vmas(next);
+}
+
 /*
  * rmap interfaces called when adding or removing pte of page
  */
+void page_move_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
 void page_add_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
 void page_add_new_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
 void page_add_file_rmap(struct page *);
@@ -181,7 +209,7 @@ static inline int page_referenced(struct page *page, int is_locked,
                                  unsigned long *vm_flags)
 {
        *vm_flags = 0;
-       return TestClearPageReferenced(page);
+       return 0;
 }
 
 #define try_to_unmap(page, refs) SWAP_FAIL
index 4b1753f7e48e328a0811de88f8e0978fdb87f490..46c6f8d5dc06d2dce20f0f6f52e1884519403651 100644 (file)
@@ -396,60 +396,6 @@ extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
 static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
 #endif
 
-#if USE_SPLIT_PTLOCKS
-/*
- * The mm counters are not protected by its page_table_lock,
- * so must be incremented atomically.
- */
-#define set_mm_counter(mm, member, value) atomic_long_set(&(mm)->_##member, value)
-#define get_mm_counter(mm, member) ((unsigned long)atomic_long_read(&(mm)->_##member))
-#define add_mm_counter(mm, member, value) atomic_long_add(value, &(mm)->_##member)
-#define inc_mm_counter(mm, member) atomic_long_inc(&(mm)->_##member)
-#define dec_mm_counter(mm, member) atomic_long_dec(&(mm)->_##member)
-
-#else  /* !USE_SPLIT_PTLOCKS */
-/*
- * The mm counters are protected by its page_table_lock,
- * so can be incremented directly.
- */
-#define set_mm_counter(mm, member, value) (mm)->_##member = (value)
-#define get_mm_counter(mm, member) ((mm)->_##member)
-#define add_mm_counter(mm, member, value) (mm)->_##member += (value)
-#define inc_mm_counter(mm, member) (mm)->_##member++
-#define dec_mm_counter(mm, member) (mm)->_##member--
-
-#endif /* !USE_SPLIT_PTLOCKS */
-
-#define get_mm_rss(mm)                                 \
-       (get_mm_counter(mm, file_rss) + get_mm_counter(mm, anon_rss))
-#define update_hiwater_rss(mm) do {                    \
-       unsigned long _rss = get_mm_rss(mm);            \
-       if ((mm)->hiwater_rss < _rss)                   \
-               (mm)->hiwater_rss = _rss;               \
-} while (0)
-#define update_hiwater_vm(mm)  do {                    \
-       if ((mm)->hiwater_vm < (mm)->total_vm)          \
-               (mm)->hiwater_vm = (mm)->total_vm;      \
-} while (0)
-
-static inline unsigned long get_mm_hiwater_rss(struct mm_struct *mm)
-{
-       return max(mm->hiwater_rss, get_mm_rss(mm));
-}
-
-static inline void setmax_mm_hiwater_rss(unsigned long *maxrss,
-                                        struct mm_struct *mm)
-{
-       unsigned long hiwater_rss = get_mm_hiwater_rss(mm);
-
-       if (*maxrss < hiwater_rss)
-               *maxrss = hiwater_rss;
-}
-
-static inline unsigned long get_mm_hiwater_vm(struct mm_struct *mm)
-{
-       return max(mm->hiwater_vm, mm->total_vm);
-}
 
 extern void set_dumpable(struct mm_struct *mm, int value);
 extern int get_dumpable(struct mm_struct *mm);
@@ -1274,7 +1220,9 @@ struct task_struct {
        struct plist_node pushable_tasks;
 
        struct mm_struct *mm, *active_mm;
-
+#if defined(SPLIT_RSS_COUNTING)
+       struct task_rss_stat    rss_stat;
+#endif
 /* task state */
        int exit_state;
        int exit_code, exit_signal;
index 1c297ddc9d5a4ef50895e9199ca7bf6fcb411182..1b177d29a7f0fdac7c2483431abb0ec92e3a2a2d 100644 (file)
@@ -2,6 +2,7 @@
 #define __LINUX_SERIAL_SCI_H
 
 #include <linux/serial_core.h>
+#include <asm/dmaengine.h>
 
 /*
  * Generic header for SuperH SCI(F) (used by sh/sh64/h8300 and related parts)
@@ -16,6 +17,8 @@ enum {
        SCIx_NR_IRQS,
 };
 
+struct device;
+
 /*
  * Platform device specific platform_data struct
  */
@@ -26,6 +29,9 @@ struct plat_sci_port {
        unsigned int    type;                   /* SCI / SCIF / IRDA */
        upf_t           flags;                  /* UPF_* flags */
        char            *clk;                   /* clock string */
+       struct device   *dma_dev;
+       enum sh_dmae_slave_chan_id dma_slave_tx;
+       enum sh_dmae_slave_chan_id dma_slave_rx;
 };
 
 #endif /* __LINUX_SERIAL_SCI_H */
index 2da8372519f5e96a32027a9be8b0efc66f35b907..488446289cab4839670d8ff507d1cfb508d2fc98 100644 (file)
 #else
 # define SLAB_NOTRACK          0x00000000UL
 #endif
+#ifdef CONFIG_FAILSLAB
+# define SLAB_FAILSLAB         0x02000000UL    /* Fault injection mark */
+#else
+# define SLAB_FAILSLAB         0x00000000UL
+#endif
 
 /* The following flags affect the page allocator grouping pages by mobility */
 #define SLAB_RECLAIM_ACCOUNT   0x00020000UL            /* Objects are reclaimable */
index 1e14beb23f9ba757c8db47e049261bbec0c9c6f3..0249d4175bacbb9d7b2a3a86758b8dfd0807a1e7 100644 (file)
@@ -38,8 +38,6 @@ struct kmem_cache_cpu {
        void **freelist;        /* Pointer to first free per cpu object */
        struct page *page;      /* The slab from which we are allocating */
        int node;               /* The node of the page (or -1 for debug) */
-       unsigned int offset;    /* Freepointer offset (in word units) */
-       unsigned int objsize;   /* Size of an object (from kmem_cache) */
 #ifdef CONFIG_SLUB_STATS
        unsigned stat[NR_SLUB_STAT_ITEMS];
 #endif
@@ -69,6 +67,7 @@ struct kmem_cache_order_objects {
  * Slab cache management.
  */
 struct kmem_cache {
+       struct kmem_cache_cpu *cpu_slab;
        /* Used for retriving partial slabs etc */
        unsigned long flags;
        int size;               /* The size of an object including meta data */
@@ -104,11 +103,6 @@ struct kmem_cache {
        int remote_node_defrag_ratio;
        struct kmem_cache_node *node[MAX_NUMNODES];
 #endif
-#ifdef CONFIG_SMP
-       struct kmem_cache_cpu *cpu_slab[NR_CPUS];
-#else
-       struct kmem_cache_cpu cpu_slab;
-#endif
 };
 
 /*
@@ -135,11 +129,21 @@ struct kmem_cache {
 
 #define SLUB_PAGE_SHIFT (PAGE_SHIFT + 2)
 
+#ifdef CONFIG_ZONE_DMA
+#define SLUB_DMA __GFP_DMA
+/* Reserve extra caches for potential DMA use */
+#define KMALLOC_CACHES (2 * SLUB_PAGE_SHIFT - 6)
+#else
+/* Disable DMA functionality */
+#define SLUB_DMA (__force gfp_t)0
+#define KMALLOC_CACHES SLUB_PAGE_SHIFT
+#endif
+
 /*
  * We keep the general caches in an array of slab caches that are used for
  * 2^x bytes of allocations.
  */
-extern struct kmem_cache kmalloc_caches[SLUB_PAGE_SHIFT];
+extern struct kmem_cache kmalloc_caches[KMALLOC_CACHES];
 
 /*
  * Sorry that the following has to be that ugly but some versions of GCC
@@ -207,13 +211,6 @@ static __always_inline struct kmem_cache *kmalloc_slab(size_t size)
        return &kmalloc_caches[index];
 }
 
-#ifdef CONFIG_ZONE_DMA
-#define SLUB_DMA __GFP_DMA
-#else
-/* Disable DMA functionality */
-#define SLUB_DMA (__force gfp_t)0
-#endif
-
 void *kmem_cache_alloc(struct kmem_cache *, gfp_t);
 void *__kmalloc(size_t size, gfp_t flags);
 
index 7a0570e6a5960cfedd82fcc16c38fb6c99947680..cfa2d20e35f152a8bad87ec32bcd27f206214c69 100644 (file)
@@ -154,7 +154,7 @@ smp_call_function_any(const struct cpumask *mask, void (*func)(void *info),
 /*
  * smp_processor_id(): get the current CPU ID.
  *
- * if DEBUG_PREEMPT is enabled the we check whether it is
+ * if DEBUG_PREEMPT is enabled then we check whether it is
  * used in a preemption-safe way. (smp_processor_id() is safe
  * if it's used in a preemption-off critical section, or in
  * a thread that is bound to the current CPU.)
index 6dfd83f19b4b1ceaa8575c25b2a6efe949e9d963..34af0a3477bf1d6fc0472c0c845f63d28e05d4a1 100644 (file)
@@ -1,9 +1,27 @@
 #ifndef LINUX_SPI_MAX7301_H
 #define LINUX_SPI_MAX7301_H
 
+#include <linux/gpio.h>
+
+/*
+ * Some registers must be read back to modify.
+ * To save time we cache them here in memory
+ */
+struct max7301 {
+       struct mutex    lock;
+       u8              port_config[8]; /* field 0 is unused */
+       u32             out_level;      /* cached output levels */
+       struct gpio_chip chip;
+       struct device *dev;
+       int (*write)(struct device *dev, unsigned int reg, unsigned int val);
+       int (*read)(struct device *dev, unsigned int reg);
+};
+
 struct max7301_platform_data {
        /* number assigned to the first GPIO */
        unsigned        base;
 };
 
+extern int __max730x_remove(struct device *dev);
+extern int __max730x_probe(struct max7301 *ts);
 #endif
index 6508f0dc0eff0119f5d9b10ab2a08ee7b75c5f27..d7152b451e21d7fb2bb84fcd3ffc33afa8ace8e9 100644 (file)
@@ -38,12 +38,27 @@ int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
 void xprt_destroy_backchannel(struct rpc_xprt *, int max_reqs);
 void bc_release_request(struct rpc_task *);
 int bc_send(struct rpc_rqst *req);
+
+/*
+ * Determine if a shared backchannel is in use
+ */
+static inline int svc_is_backchannel(const struct svc_rqst *rqstp)
+{
+       if (rqstp->rq_server->bc_xprt)
+               return 1;
+       return 0;
+}
 #else /* CONFIG_NFS_V4_1 */
 static inline int xprt_setup_backchannel(struct rpc_xprt *xprt,
                                         unsigned int min_reqs)
 {
        return 0;
 }
+
+static inline int svc_is_backchannel(const struct svc_rqst *rqstp)
+{
+       return 0;
+}
 #endif /* CONFIG_NFS_V4_1 */
 #endif /* _LINUX_SUNRPC_BC_XPRT_H */
 
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
new file mode 100644 (file)
index 0000000..ae9ab13
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Red Hat Inc.
+ * Author : Dave Airlie <airlied@redhat.com>
+ *
+ * Licensed under GPLv2
+ *
+ * vga_switcheroo.h - Support for laptop with dual GPU using one set of outputs
+ */
+
+#include <linux/fb.h>
+
+enum vga_switcheroo_state {
+       VGA_SWITCHEROO_OFF,
+       VGA_SWITCHEROO_ON,
+};
+
+enum vga_switcheroo_client_id {
+       VGA_SWITCHEROO_IGD,
+       VGA_SWITCHEROO_DIS,
+       VGA_SWITCHEROO_MAX_CLIENTS,
+};
+
+struct vga_switcheroo_handler {
+       int (*switchto)(enum vga_switcheroo_client_id id);
+       int (*power_state)(enum vga_switcheroo_client_id id,
+                          enum vga_switcheroo_state state);
+       int (*init)(void);
+       int (*get_client_id)(struct pci_dev *pdev);
+};
+
+
+#if defined(CONFIG_VGA_SWITCHEROO)
+void vga_switcheroo_unregister_client(struct pci_dev *dev);
+int vga_switcheroo_register_client(struct pci_dev *dev,
+                                  void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state),
+                                  bool (*can_switch)(struct pci_dev *dev));
+
+void vga_switcheroo_client_fb_set(struct pci_dev *dev,
+                                 struct fb_info *info);
+
+int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler);
+void vga_switcheroo_unregister_handler(void);
+
+int vga_switcheroo_process_delayed_switch(void);
+
+#else
+
+static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
+static inline int vga_switcheroo_register_client(struct pci_dev *dev,
+                                         void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state),
+                                         bool (*can_switch)(struct pci_dev *dev)) { return 0; }
+static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {}
+static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; }
+static inline void vga_switcheroo_unregister_handler(void) {}
+static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
+
+#endif
index 095e10d148b40d32780621dec6f259a5c90c22e1..3322750800834ca854bff555a03d567d291a02e2 100644 (file)
@@ -5,7 +5,4 @@
 #include <linux/virtio_ids.h>
 #include <linux/virtio_config.h>
 
-/* Maximum number of virtio channels per partition (1 for now) */
-#define MAX_9P_CHAN    1
-
 #endif /* _LINUX_VIRTIO_9P_H */
index fb00b329f0d3dd91d3998871f4dc080d9b4f2065..52e1fff709e43fbd1f825809036994b5aaa76a87 100644 (file)
 /* Number of requests per row */
 #define P9_ROW_MAXTAG 255
 
+/** enum p9_proto_versions - 9P protocol versions
+ * @p9_proto_legacy: 9P Legacy mode, pre-9P2000.u
+ * @p9_proto_2000u: 9P2000.u extension
+ * @p9_proto_2010L: 9P2010.L extension
+ */
+
+enum p9_proto_versions{
+       p9_proto_legacy = 0,
+       p9_proto_2000u = 1,
+       p9_proto_2010L = 2,
+};
+
+
 /**
  * enum p9_trans_status - different states of underlying transports
  * @Connected: transport is connected and healthy
@@ -111,6 +124,7 @@ struct p9_req_t {
  * @lock: protect @fidlist
  * @msize: maximum data size negotiated by protocol
  * @dotu: extension flags negotiated by protocol
+ * @proto_version: 9P protocol version to use
  * @trans_mod: module API instantiated with this client
  * @trans: tranport instance state and API
  * @conn: connection state information used by trans_fd
@@ -137,7 +151,7 @@ struct p9_req_t {
 struct p9_client {
        spinlock_t lock; /* protect client structure */
        int msize;
-       unsigned char dotu;
+       unsigned char proto_version;
        struct p9_trans_module *trans_mod;
        enum p9_trans_status status;
        void *trans;
@@ -209,5 +223,7 @@ int p9_parse_header(struct p9_fcall *, int32_t *, int8_t *, int16_t *, int);
 int p9stat_read(char *, int, struct p9_wstat *, int);
 void p9stat_free(struct p9_wstat *);
 
+int p9_is_proto_dotu(struct p9_client *clnt);
+int p9_is_proto_dotl(struct p9_client *clnt);
 
 #endif /* NET_9P_CLIENT_H */
index d0b6cd3afb2fb443d5bd4220f94b5bb698131ea9..2aa6aa3e8f613e038df5a595c4952ae721f14102 100644 (file)
@@ -874,6 +874,107 @@ TRACE_EVENT(ext4_forget,
                  __entry->mode, __entry->is_metadata, __entry->block)
 );
 
+TRACE_EVENT(ext4_da_update_reserve_space,
+       TP_PROTO(struct inode *inode, int used_blocks),
+
+       TP_ARGS(inode, used_blocks),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        ino_t,  ino                     )
+               __field(        umode_t, mode                   )
+               __field(        __u64,  i_blocks                )
+               __field(        int,    used_blocks             )
+               __field(        int,    reserved_data_blocks    )
+               __field(        int,    reserved_meta_blocks    )
+               __field(        int,    allocated_meta_blocks   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = inode->i_sb->s_dev;
+               __entry->ino    = inode->i_ino;
+               __entry->mode   = inode->i_mode;
+               __entry->i_blocks = inode->i_blocks;
+               __entry->used_blocks = used_blocks;
+               __entry->reserved_data_blocks = EXT4_I(inode)->i_reserved_data_blocks;
+               __entry->reserved_meta_blocks = EXT4_I(inode)->i_reserved_meta_blocks;
+               __entry->allocated_meta_blocks = EXT4_I(inode)->i_allocated_meta_blocks;
+       ),
+
+       TP_printk("dev %s ino %lu mode 0%o i_blocks %llu used_blocks %d reserved_data_blocks %d reserved_meta_blocks %d allocated_meta_blocks %d",
+                 jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
+                 __entry->mode,  (unsigned long long) __entry->i_blocks,
+                 __entry->used_blocks, __entry->reserved_data_blocks,
+                 __entry->reserved_meta_blocks, __entry->allocated_meta_blocks)
+);
+
+TRACE_EVENT(ext4_da_reserve_space,
+       TP_PROTO(struct inode *inode, int md_needed),
+
+       TP_ARGS(inode, md_needed),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        ino_t,  ino                     )
+               __field(        umode_t, mode                   )
+               __field(        __u64,  i_blocks                )
+               __field(        int,    md_needed               )
+               __field(        int,    reserved_data_blocks    )
+               __field(        int,    reserved_meta_blocks    )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = inode->i_sb->s_dev;
+               __entry->ino    = inode->i_ino;
+               __entry->mode   = inode->i_mode;
+               __entry->i_blocks = inode->i_blocks;
+               __entry->md_needed = md_needed;
+               __entry->reserved_data_blocks = EXT4_I(inode)->i_reserved_data_blocks;
+               __entry->reserved_meta_blocks = EXT4_I(inode)->i_reserved_meta_blocks;
+       ),
+
+       TP_printk("dev %s ino %lu mode 0%o i_blocks %llu md_needed %d reserved_data_blocks %d reserved_meta_blocks %d",
+                 jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
+                 __entry->mode, (unsigned long long) __entry->i_blocks,
+                 __entry->md_needed, __entry->reserved_data_blocks,
+                 __entry->reserved_meta_blocks)
+);
+
+TRACE_EVENT(ext4_da_release_space,
+       TP_PROTO(struct inode *inode, int freed_blocks),
+
+       TP_ARGS(inode, freed_blocks),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        ino_t,  ino                     )
+               __field(        umode_t, mode                   )
+               __field(        __u64,  i_blocks                )
+               __field(        int,    freed_blocks            )
+               __field(        int,    reserved_data_blocks    )
+               __field(        int,    reserved_meta_blocks    )
+               __field(        int,    allocated_meta_blocks   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = inode->i_sb->s_dev;
+               __entry->ino    = inode->i_ino;
+               __entry->mode   = inode->i_mode;
+               __entry->i_blocks = inode->i_blocks;
+               __entry->freed_blocks = freed_blocks;
+               __entry->reserved_data_blocks = EXT4_I(inode)->i_reserved_data_blocks;
+               __entry->reserved_meta_blocks = EXT4_I(inode)->i_reserved_meta_blocks;
+               __entry->allocated_meta_blocks = EXT4_I(inode)->i_allocated_meta_blocks;
+       ),
+
+       TP_printk("dev %s ino %lu mode 0%o i_blocks %llu freed_blocks %d reserved_data_blocks %d reserved_meta_blocks %d allocated_meta_blocks %d",
+                 jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
+                 __entry->mode, (unsigned long long) __entry->i_blocks,
+                 __entry->freed_blocks, __entry->reserved_data_blocks,
+                 __entry->reserved_meta_blocks, __entry->allocated_meta_blocks)
+);
+
+
 #endif /* _TRACE_EXT4_H */
 
 /* This part must be outside protection */
index 96b370a050deb27ca6fe8393990e52734dbbff09..bf16545cc97756d263305f17712f522b979bbf32 100644 (file)
@@ -199,6 +199,34 @@ TRACE_EVENT(jbd2_checkpoint_stats,
                  __entry->forced_to_close, __entry->written, __entry->dropped)
 );
 
+TRACE_EVENT(jbd2_cleanup_journal_tail,
+
+       TP_PROTO(journal_t *journal, tid_t first_tid,
+                unsigned long block_nr, unsigned long freed),
+
+       TP_ARGS(journal, first_tid, block_nr, freed),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        tid_t,  tail_sequence           )
+               __field(        tid_t,  first_tid               )
+               __field(unsigned long,  block_nr                )
+               __field(unsigned long,  freed                   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = journal->j_fs_dev->bd_dev;
+               __entry->tail_sequence  = journal->j_tail_sequence;
+               __entry->first_tid      = first_tid;
+               __entry->block_nr       = block_nr;
+               __entry->freed          = freed;
+       ),
+
+       TP_printk("dev %s from %u to %u offset %lu freed %lu",
+                 jbd2_dev_to_name(__entry->dev), __entry->tail_sequence,
+                 __entry->first_tid, __entry->block_nr, __entry->freed)
+);
+
 #endif /* _TRACE_JBD2_H */
 
 /* This part must be outside protection */
index dbe1084552757da03d47a8ee64e98581974a14a3..b17d49dfc3ef7dc55946726d432162f1f5b0501d 100644 (file)
@@ -145,6 +145,47 @@ TRACE_EVENT(kvm_mmio,
                  __entry->len, __entry->gpa, __entry->val)
 );
 
+#define kvm_fpu_load_symbol    \
+       {0, "unload"},          \
+       {1, "load"}
+
+TRACE_EVENT(kvm_fpu,
+       TP_PROTO(int load),
+       TP_ARGS(load),
+
+       TP_STRUCT__entry(
+               __field(        u32,            load            )
+       ),
+
+       TP_fast_assign(
+               __entry->load           = load;
+       ),
+
+       TP_printk("%s", __print_symbolic(__entry->load, kvm_fpu_load_symbol))
+);
+
+TRACE_EVENT(kvm_age_page,
+       TP_PROTO(ulong hva, struct kvm_memory_slot *slot, int ref),
+       TP_ARGS(hva, slot, ref),
+
+       TP_STRUCT__entry(
+               __field(        u64,    hva             )
+               __field(        u64,    gfn             )
+               __field(        u8,     referenced      )
+       ),
+
+       TP_fast_assign(
+               __entry->hva            = hva;
+               __entry->gfn            =
+                 slot->base_gfn + ((hva - slot->userspace_addr) >> PAGE_SHIFT);
+               __entry->referenced     = ref;
+       ),
+
+       TP_printk("hva %llx gfn %llx %s",
+                 __entry->hva, __entry->gfn,
+                 __entry->referenced ? "YOUNG" : "OLD")
+);
+
 #endif /* _TRACE_KVM_MAIN_H */
 
 /* This part must be outside protection */
index 614241b5200cf906c066288ac15a42df835490ce..2b108538d0d95684be2048fcc80a7acf484970c4 100644 (file)
@@ -30,11 +30,7 @@ static int __init do_linuxrc(void * shell)
        extern char * envp_init[];
 
        sys_close(old_fd);sys_close(root_fd);
-       sys_close(0);sys_close(1);sys_close(2);
        sys_setsid();
-       (void) sys_open("/dev/console",O_RDWR,0);
-       (void) sys_dup(0);
-       (void) sys_dup(0);
        return kernel_execve(shell, argv, envp_init);
 }
 
index b37d34beb90bb0b3f490731d641ab3a2a07e45d7..37d3859b1b32eda85e12ef7c0f1a159b32eafe4c 100644 (file)
@@ -525,7 +525,7 @@ static void __init clean_rootfs(void)
        int fd;
        void *buf;
        struct linux_dirent64 *dirp;
-       int count;
+       int num;
 
        fd = sys_open("/", O_RDONLY, 0);
        WARN_ON(fd < 0);
@@ -539,9 +539,9 @@ static void __init clean_rootfs(void)
        }
 
        dirp = buf;
-       count = sys_getdents64(fd, dirp, BUF_SIZE);
-       while (count > 0) {
-               while (count > 0) {
+       num = sys_getdents64(fd, dirp, BUF_SIZE);
+       while (num > 0) {
+               while (num > 0) {
                        struct stat st;
                        int ret;
 
@@ -554,12 +554,12 @@ static void __init clean_rootfs(void)
                                        sys_unlink(dirp->d_name);
                        }
 
-                       count -= dirp->d_reclen;
+                       num -= dirp->d_reclen;
                        dirp = (void *)dirp + dirp->d_reclen;
                }
                dirp = buf;
                memset(buf, 0, BUF_SIZE);
-               count = sys_getdents64(fd, dirp, BUF_SIZE);
+               num = sys_getdents64(fd, dirp, BUF_SIZE);
        }
 
        sys_close(fd);
index 18098153c33170c782309f6275cb7f936cdcf6b0..a1ab78ceb4b62abf17551bc990a3298d1c95a320 100644 (file)
@@ -174,7 +174,7 @@ static int __init maxcpus(char *str)
 
 early_param("maxcpus", maxcpus);
 #else
-const unsigned int setup_max_cpus = NR_CPUS;
+static const unsigned int setup_max_cpus = NR_CPUS;
 #endif
 
 /*
@@ -618,7 +618,7 @@ asmlinkage void __init start_kernel(void)
        local_irq_enable();
 
        /* Interrupts are enabled now so all GFP allocations are safe. */
-       set_gfp_allowed_mask(__GFP_BITS_MASK);
+       gfp_allowed_mask = __GFP_BITS_MASK;
 
        kmem_cache_init_late();
 
@@ -822,11 +822,6 @@ static noinline int init_post(void)
        system_state = SYSTEM_RUNNING;
        numa_default_policy();
 
-       if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
-               printk(KERN_WARNING "Warning: unable to open an initial console.\n");
-
-       (void) sys_dup(0);
-       (void) sys_dup(0);
 
        current->signal->flags |= SIGNAL_UNKILLABLE;
 
@@ -852,7 +847,8 @@ static noinline int init_post(void)
        run_init_process("/bin/init");
        run_init_process("/bin/sh");
 
-       panic("No init found.  Try passing init= option to kernel.");
+       panic("No init found.  Try passing init= option to kernel. "
+             "See Linux Documentation/init.txt for guidance.");
 }
 
 static int __init kernel_init(void * unused)
@@ -889,6 +885,12 @@ static int __init kernel_init(void * unused)
 
        do_basic_setup();
 
+       /* Open the /dev/console on the rootfs, this should never fail */
+       if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
+               printk(KERN_WARNING "Warning: unable to open an initial console.\n");
+
+       (void) sys_dup(0);
+       (void) sys_dup(0);
        /*
         * check if there is an early userspace init.  If yes, let it do all
         * the work
index c79bd57353e73e0e68af8ca8fe1c41da787c7a61..b6cb06451f4be9295608a4156d3c32809773919a 100644 (file)
@@ -134,7 +134,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
                        init_waitqueue_head(&info->wait_q);
                        INIT_LIST_HEAD(&info->e_wait_q[0].list);
                        INIT_LIST_HEAD(&info->e_wait_q[1].list);
-                       info->messages = NULL;
                        info->notify_owner = NULL;
                        info->qsize = 0;
                        info->user = NULL;      /* set when all is ok */
@@ -146,6 +145,10 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
                                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;
+
                        mq_bytes = (mq_msg_tblsz +
                                (info->attr.mq_maxmsg * info->attr.mq_msgsize));
 
@@ -154,18 +157,12 @@ static struct inode *mqueue_get_inode(struct super_block *sb,
                            u->mq_bytes + mq_bytes >
                            p->signal->rlim[RLIMIT_MSGQUEUE].rlim_cur) {
                                spin_unlock(&mq_lock);
+                               kfree(info->messages);
                                goto out_inode;
                        }
                        u->mq_bytes += mq_bytes;
                        spin_unlock(&mq_lock);
 
-                       info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL);
-                       if (!info->messages) {
-                               spin_lock(&mq_lock);
-                               u->mq_bytes -= mq_bytes;
-                               spin_unlock(&mq_lock);
-                               goto out_inode;
-                       }
                        /* all is ok */
                        info->user = get_uid(u);
                } else if (S_ISDIR(mode)) {
@@ -187,7 +184,7 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
        struct ipc_namespace *ns = data;
-       int error = 0;
+       int error;
 
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -205,7 +202,9 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent)
        if (!sb->s_root) {
                iput(inode);
                error = -ENOMEM;
+               goto out;
        }
+       error = 0;
 
 out:
        return error;
@@ -264,8 +263,9 @@ static void mqueue_delete_inode(struct inode *inode)
 
        clear_inode(inode);
 
-       mq_bytes = (info->attr.mq_maxmsg * sizeof(struct msg_msg *) +
-                  (info->attr.mq_maxmsg * info->attr.mq_msgsize));
+       /* Total amount of bytes accounted for the mqueue */
+       mq_bytes = info->attr.mq_maxmsg * (sizeof(struct msg_msg *)
+           + info->attr.mq_msgsize);
        user = info->user;
        if (user) {
                spin_lock(&mq_lock);
@@ -604,8 +604,8 @@ static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr)
        /* check for overflow */
        if (attr->mq_msgsize > ULONG_MAX/attr->mq_maxmsg)
                return 0;
-       if ((unsigned long)(attr->mq_maxmsg * attr->mq_msgsize) +
-           (attr->mq_maxmsg * sizeof (struct msg_msg *)) <
+       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;
@@ -623,9 +623,10 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir,
        int ret;
 
        if (attr) {
-               ret = -EINVAL;
-               if (!mq_attr_ok(ipc_ns, attr))
+               if (!mq_attr_ok(ipc_ns, attr)) {
+                       ret = -EINVAL;
                        goto out;
+               }
                /* store for use during create */
                dentry->d_fsdata = attr;
        }
@@ -659,24 +660,28 @@ out:
 static struct file *do_open(struct ipc_namespace *ipc_ns,
                                struct dentry *dentry, int oflag)
 {
+       int ret;
        const struct cred *cred = current_cred();
 
        static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
                                                  MAY_READ | MAY_WRITE };
 
        if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
-               dput(dentry);
-               mntput(ipc_ns->mq_mnt);
-               return ERR_PTR(-EINVAL);
+               ret = -EINVAL;
+               goto err;
        }
 
        if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) {
-               dput(dentry);
-               mntput(ipc_ns->mq_mnt);
-               return ERR_PTR(-EACCES);
+               ret = -EACCES;
+               goto err;
        }
 
        return dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred);
+
+err:
+       dput(dentry);
+       mntput(ipc_ns->mq_mnt);
+       return ERR_PTR(ret);
 }
 
 SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
@@ -705,16 +710,17 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
        dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
        if (IS_ERR(dentry)) {
                error = PTR_ERR(dentry);
-               goto out_err;
+               goto out_putfd;
        }
        mntget(ipc_ns->mq_mnt);
 
        if (oflag & O_CREAT) {
                if (dentry->d_inode) {  /* entry already exists */
                        audit_inode(name, dentry);
-                       error = -EEXIST;
-                       if (oflag & O_EXCL)
+                       if (oflag & O_EXCL) {
+                               error = -EEXIST;
                                goto out;
+                       }
                        filp = do_open(ipc_ns, dentry, oflag);
                } else {
                        filp = do_create(ipc_ns, ipc_ns->mq_mnt->mnt_root,
@@ -722,9 +728,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, mode_t, mode,
                                                u_attr ? &attr : NULL);
                }
        } else {
-               error = -ENOENT;
-               if (!dentry->d_inode)
+               if (!dentry->d_inode) {
+                       error = -ENOENT;
                        goto out;
+               }
                audit_inode(name, dentry);
                filp = do_open(ipc_ns, dentry, oflag);
        }
@@ -742,7 +749,6 @@ out:
        mntput(ipc_ns->mq_mnt);
 out_putfd:
        put_unused_fd(fd);
-out_err:
        fd = error;
 out_upsem:
        mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
@@ -872,19 +878,24 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr,
        audit_mq_sendrecv(mqdes, msg_len, msg_prio, p);
        timeout = prepare_timeout(p);
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (unlikely(!filp))
+       if (unlikely(!filp)) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
        audit_inode(NULL, filp->f_path.dentry);
 
-       if (unlikely(!(filp->f_mode & FMODE_WRITE)))
+       if (unlikely(!(filp->f_mode & FMODE_WRITE))) {
+               ret = -EBADF;
                goto out_fput;
+       }
 
        if (unlikely(msg_len > info->attr.mq_msgsize)) {
                ret = -EMSGSIZE;
@@ -961,19 +972,24 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
        audit_mq_sendrecv(mqdes, msg_len, 0, p);
        timeout = prepare_timeout(p);
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (unlikely(!filp))
+       if (unlikely(!filp)) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
        audit_inode(NULL, filp->f_path.dentry);
 
-       if (unlikely(!(filp->f_mode & FMODE_READ)))
+       if (unlikely(!(filp->f_mode & FMODE_READ))) {
+               ret = -EBADF;
                goto out_fput;
+       }
 
        /* checks if buffer is big enough */
        if (unlikely(msg_len < info->attr.mq_msgsize)) {
@@ -1063,13 +1079,14 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
 
                        /* create the notify skb */
                        nc = alloc_skb(NOTIFY_COOKIE_LEN, GFP_KERNEL);
-                       ret = -ENOMEM;
-                       if (!nc)
+                       if (!nc) {
+                               ret = -ENOMEM;
                                goto out;
-                       ret = -EFAULT;
+                       }
                        if (copy_from_user(nc->data,
                                        notification.sigev_value.sival_ptr,
                                        NOTIFY_COOKIE_LEN)) {
+                               ret = -EFAULT;
                                goto out;
                        }
 
@@ -1078,9 +1095,10 @@ SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes,
                        /* and attach it to the socket */
 retry:
                        filp = fget(notification.sigev_signo);
-                       ret = -EBADF;
-                       if (!filp)
+                       if (!filp) {
+                               ret = -EBADF;
                                goto out;
+                       }
                        sock = netlink_getsockbyfilp(filp);
                        fput(filp);
                        if (IS_ERR(sock)) {
@@ -1092,7 +1110,7 @@ retry:
                        timeo = MAX_SCHEDULE_TIMEOUT;
                        ret = netlink_attachskb(sock, nc, &timeo, NULL);
                        if (ret == 1)
-                               goto retry;
+                               goto retry;
                        if (ret) {
                                sock = NULL;
                                nc = NULL;
@@ -1101,14 +1119,17 @@ retry:
                }
        }
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (!filp)
+       if (!filp) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
 
        ret = 0;
@@ -1171,14 +1192,17 @@ SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes,
                        return -EINVAL;
        }
 
-       ret = -EBADF;
        filp = fget(mqdes);
-       if (!filp)
+       if (!filp) {
+               ret = -EBADF;
                goto out;
+       }
 
        inode = filp->f_path.dentry->d_inode;
-       if (unlikely(filp->f_op != &mqueue_file_operations))
+       if (unlikely(filp->f_op != &mqueue_file_operations)) {
+               ret = -EBADF;
                goto out_fput;
+       }
        info = MQUEUE_I(inode);
 
        spin_lock(&info->lock);
@@ -1272,7 +1296,7 @@ static int __init init_mqueue_fs(void)
        if (mqueue_inode_cachep == NULL)
                return -ENOMEM;
 
-       /* ignore failues - they are not fatal */
+       /* ignore failures - they are not fatal */
        mq_sysctl_table = mq_register_sysctl_table();
 
        error = register_filesystem(&mqueue_fs_type);
index 7b974699f8c22f0e94bb96d1c3d6d1f80e5b88f5..a987aa1676b594b5501efd7213ccf671318c94c9 100644 (file)
@@ -91,6 +91,9 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
 obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
 obj-$(CONFIG_TRACEPOINTS) += tracepoint.o
 obj-$(CONFIG_LATENCYTOP) += latencytop.o
+obj-$(CONFIG_BINFMT_ELF) += elfcore.o
+obj-$(CONFIG_COMPAT_BINFMT_ELF) += elfcore.o
+obj-$(CONFIG_BINFMT_ELF_FDPIC) += elfcore.o
 obj-$(CONFIG_FUNCTION_TRACER) += trace/
 obj-$(CONFIG_TRACING) += trace/
 obj-$(CONFIG_X86_DS) += trace/
index 4b05bd9479dbc3d20cee5d658407d4cccb4a4727..028e85663f273d1d1cab2d75cde3ef76b22d3d48 100644 (file)
@@ -548,6 +548,11 @@ int audit_remove_tree_rule(struct audit_krule *rule)
        return 0;
 }
 
+static int compare_root(struct vfsmount *mnt, void *arg)
+{
+       return mnt->mnt_root->d_inode == arg;
+}
+
 void audit_trim_trees(void)
 {
        struct list_head cursor;
@@ -559,7 +564,6 @@ void audit_trim_trees(void)
                struct path path;
                struct vfsmount *root_mnt;
                struct node *node;
-               struct list_head list;
                int err;
 
                tree = container_of(cursor.next, struct audit_tree, list);
@@ -577,24 +581,16 @@ void audit_trim_trees(void)
                if (!root_mnt)
                        goto skip_it;
 
-               list_add_tail(&list, &root_mnt->mnt_list);
                spin_lock(&hash_lock);
                list_for_each_entry(node, &tree->chunks, list) {
-                       struct audit_chunk *chunk = find_chunk(node);
-                       struct inode *inode = chunk->watch.inode;
-                       struct vfsmount *mnt;
+                       struct inode *inode = find_chunk(node)->watch.inode;
                        node->index |= 1U<<31;
-                       list_for_each_entry(mnt, &list, mnt_list) {
-                               if (mnt->mnt_root->d_inode == inode) {
-                                       node->index &= ~(1U<<31);
-                                       break;
-                               }
-                       }
+                       if (iterate_mounts(compare_root, inode, root_mnt))
+                               node->index &= ~(1U<<31);
                }
                spin_unlock(&hash_lock);
                trim_marked(tree);
                put_tree(tree);
-               list_del_init(&list);
                drop_collected_mounts(root_mnt);
 skip_it:
                mutex_lock(&audit_filter_mutex);
@@ -603,22 +599,6 @@ skip_it:
        mutex_unlock(&audit_filter_mutex);
 }
 
-static int is_under(struct vfsmount *mnt, struct dentry *dentry,
-                   struct path *path)
-{
-       if (mnt != path->mnt) {
-               for (;;) {
-                       if (mnt->mnt_parent == mnt)
-                               return 0;
-                       if (mnt->mnt_parent == path->mnt)
-                                       break;
-                       mnt = mnt->mnt_parent;
-               }
-               dentry = mnt->mnt_mountpoint;
-       }
-       return is_subdir(dentry, path->dentry);
-}
-
 int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
 {
 
@@ -638,13 +618,17 @@ void audit_put_tree(struct audit_tree *tree)
        put_tree(tree);
 }
 
+static int tag_mount(struct vfsmount *mnt, void *arg)
+{
+       return tag_chunk(mnt->mnt_root->d_inode, arg);
+}
+
 /* called with audit_filter_mutex */
 int audit_add_tree_rule(struct audit_krule *rule)
 {
        struct audit_tree *seed = rule->tree, *tree;
        struct path path;
-       struct vfsmount *mnt, *p;
-       struct list_head list;
+       struct vfsmount *mnt;
        int err;
 
        list_for_each_entry(tree, &tree_list, list) {
@@ -670,16 +654,9 @@ int audit_add_tree_rule(struct audit_krule *rule)
                err = -ENOMEM;
                goto Err;
        }
-       list_add_tail(&list, &mnt->mnt_list);
 
        get_tree(tree);
-       list_for_each_entry(p, &list, mnt_list) {
-               err = tag_chunk(p->mnt_root->d_inode, tree);
-               if (err)
-                       break;
-       }
-
-       list_del(&list);
+       err = iterate_mounts(tag_mount, tree, mnt);
        drop_collected_mounts(mnt);
 
        if (!err) {
@@ -714,31 +691,23 @@ int audit_tag_tree(char *old, char *new)
 {
        struct list_head cursor, barrier;
        int failed = 0;
-       struct path path;
+       struct path path1, path2;
        struct vfsmount *tagged;
-       struct list_head list;
-       struct vfsmount *mnt;
-       struct dentry *dentry;
        int err;
 
-       err = kern_path(new, 0, &path);
+       err = kern_path(new, 0, &path2);
        if (err)
                return err;
-       tagged = collect_mounts(&path);
-       path_put(&path);
+       tagged = collect_mounts(&path2);
+       path_put(&path2);
        if (!tagged)
                return -ENOMEM;
 
-       err = kern_path(old, 0, &path);
+       err = kern_path(old, 0, &path1);
        if (err) {
                drop_collected_mounts(tagged);
                return err;
        }
-       mnt = mntget(path.mnt);
-       dentry = dget(path.dentry);
-       path_put(&path);
-
-       list_add_tail(&list, &tagged->mnt_list);
 
        mutex_lock(&audit_filter_mutex);
        list_add(&barrier, &tree_list);
@@ -746,7 +715,7 @@ int audit_tag_tree(char *old, char *new)
 
        while (cursor.next != &tree_list) {
                struct audit_tree *tree;
-               struct vfsmount *p;
+               int good_one = 0;
 
                tree = container_of(cursor.next, struct audit_tree, list);
                get_tree(tree);
@@ -754,30 +723,19 @@ int audit_tag_tree(char *old, char *new)
                list_add(&cursor, &tree->list);
                mutex_unlock(&audit_filter_mutex);
 
-               err = kern_path(tree->pathname, 0, &path);
-               if (err) {
-                       put_tree(tree);
-                       mutex_lock(&audit_filter_mutex);
-                       continue;
+               err = kern_path(tree->pathname, 0, &path2);
+               if (!err) {
+                       good_one = path_is_under(&path1, &path2);
+                       path_put(&path2);
                }
 
-               spin_lock(&vfsmount_lock);
-               if (!is_under(mnt, dentry, &path)) {
-                       spin_unlock(&vfsmount_lock);
-                       path_put(&path);
+               if (!good_one) {
                        put_tree(tree);
                        mutex_lock(&audit_filter_mutex);
                        continue;
                }
-               spin_unlock(&vfsmount_lock);
-               path_put(&path);
-
-               list_for_each_entry(p, &list, mnt_list) {
-                       failed = tag_chunk(p->mnt_root->d_inode, tree);
-                       if (failed)
-                               break;
-               }
 
+               failed = iterate_mounts(tag_mount, tree, tagged);
                if (failed) {
                        put_tree(tree);
                        mutex_lock(&audit_filter_mutex);
@@ -818,10 +776,8 @@ int audit_tag_tree(char *old, char *new)
        }
        list_del(&barrier);
        list_del(&cursor);
-       list_del(&list);
        mutex_unlock(&audit_filter_mutex);
-       dput(dentry);
-       mntput(mnt);
+       path_put(&path1);
        drop_collected_mounts(tagged);
        return failed;
 }
index fc0f928167e78b49d0a96b6a4f6e15e80ed90c6b..f3a461c0970a32b44fd8db7da5b50778b728ab88 100644 (file)
@@ -1988,7 +1988,6 @@ void __audit_inode(const char *name, const struct dentry *dentry)
 
 /**
  * audit_inode_child - collect inode info for created/removed objects
- * @dname: inode's dentry name
  * @dentry: dentry being audited
  * @parent: inode of dentry parent
  *
@@ -2000,13 +1999,14 @@ void __audit_inode(const char *name, const struct dentry *dentry)
  * must be hooked prior, in order to capture the target inode during
  * unsuccessful attempts.
  */
-void __audit_inode_child(const char *dname, const struct dentry *dentry,
+void __audit_inode_child(const struct dentry *dentry,
                         const struct inode *parent)
 {
        int idx;
        struct audit_context *context = current->audit_context;
        const char *found_parent = NULL, *found_child = NULL;
        const struct inode *inode = dentry->d_inode;
+       const char *dname = dentry->d_name.name;
        int dirlen = 0;
 
        if (!context->in_syscall)
@@ -2014,9 +2014,6 @@ void __audit_inode_child(const char *dname, const struct dentry *dentry,
 
        if (inode)
                handle_one(inode);
-       /* determine matching parent */
-       if (!dname)
-               goto add_names;
 
        /* parent is more likely, look for it first */
        for (idx = 0; idx < context->name_count; idx++) {
index 677f25376a381ed909d431267ab891524313fe2a..f8cced2692b3799e26710ccbde3bc8f0f8b654e9 100644 (file)
@@ -338,7 +338,7 @@ int __cpuinit cpu_up(unsigned int cpu)
        if (!cpu_possible(cpu)) {
                printk(KERN_ERR "can't online cpu %d because it is not "
                        "configured as may-hotadd at boot time\n", cpu);
-#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
+#if defined(CONFIG_IA64)
                printk(KERN_ERR "please check additional_cpus= boot "
                                "parameter\n");
 #endif
diff --git a/kernel/elfcore.c b/kernel/elfcore.c
new file mode 100644 (file)
index 0000000..ff915ef
--- /dev/null
@@ -0,0 +1,28 @@
+#include <linux/elf.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+
+#include <asm/elf.h>
+
+
+Elf_Half __weak elf_core_extra_phdrs(void)
+{
+       return 0;
+}
+
+int __weak elf_core_write_extra_phdrs(struct file *file, loff_t offset, size_t *size,
+                                     unsigned long limit)
+{
+       return 1;
+}
+
+int __weak elf_core_write_extra_data(struct file *file, size_t *size,
+                                    unsigned long limit)
+{
+       return 1;
+}
+
+size_t __weak elf_core_extra_data_size(void)
+{
+       return 0;
+}
index 45ed043b8bf59b8704b7cc13f19b1b13824b7ce4..ce1e48c2d93d3dbae4769ecc518066df383f0310 100644 (file)
@@ -952,7 +952,8 @@ NORET_TYPE void do_exit(long code)
                                preempt_count());
 
        acct_update_integrals(tsk);
-
+       /* sync mm's RSS info before statistics gathering */
+       sync_mm_rss(tsk, tsk->mm);
        group_dead = atomic_dec_and_test(&tsk->signal->live);
        if (group_dead) {
                hrtimer_cancel(&tsk->signal->real_timer);
@@ -1188,7 +1189,7 @@ static int wait_task_zombie(struct wait_opts *wo, struct task_struct *p)
 
        if (unlikely(wo->wo_flags & WNOWAIT)) {
                int exit_code = p->exit_code;
-               int why, status;
+               int why;
 
                get_task_struct(p);
                read_unlock(&tasklist_lock);
index 17bbf093356d085123b83fa93c05a89e380fb53e..b0ec34abc0bb8261c5c930f1dcf78ed7203051e4 100644 (file)
@@ -329,15 +329,17 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
                if (!tmp)
                        goto fail_nomem;
                *tmp = *mpnt;
+               INIT_LIST_HEAD(&tmp->anon_vma_chain);
                pol = mpol_dup(vma_policy(mpnt));
                retval = PTR_ERR(pol);
                if (IS_ERR(pol))
                        goto fail_nomem_policy;
                vma_set_policy(tmp, pol);
+               if (anon_vma_fork(tmp, mpnt))
+                       goto fail_nomem_anon_vma_fork;
                tmp->vm_flags &= ~VM_LOCKED;
                tmp->vm_mm = mm;
                tmp->vm_next = NULL;
-               anon_vma_link(tmp);
                file = tmp->vm_file;
                if (file) {
                        struct inode *inode = file->f_path.dentry->d_inode;
@@ -392,6 +394,8 @@ out:
        flush_tlb_mm(oldmm);
        up_write(&oldmm->mmap_sem);
        return retval;
+fail_nomem_anon_vma_fork:
+       mpol_put(pol);
 fail_nomem_policy:
        kmem_cache_free(vm_area_cachep, tmp);
 fail_nomem:
@@ -455,8 +459,7 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
                (current->mm->flags & MMF_INIT_MASK) : default_dump_filter;
        mm->core_state = NULL;
        mm->nr_ptes = 0;
-       set_mm_counter(mm, file_rss, 0);
-       set_mm_counter(mm, anon_rss, 0);
+       memset(&mm->rss_stat, 0, sizeof(mm->rss_stat));
        spin_lock_init(&mm->page_table_lock);
        mm->free_area_cache = TASK_UNMAPPED_BASE;
        mm->cached_hole_size = ~0UL;
@@ -825,6 +828,8 @@ void __cleanup_sighand(struct sighand_struct *sighand)
  */
 static void posix_cpu_timers_init_group(struct signal_struct *sig)
 {
+       unsigned long cpu_limit;
+
        /* Thread group counters. */
        thread_group_cputime_init(sig);
 
@@ -839,9 +844,9 @@ static void posix_cpu_timers_init_group(struct signal_struct *sig)
        sig->cputime_expires.virt_exp = cputime_zero;
        sig->cputime_expires.sched_exp = 0;
 
-       if (sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
-               sig->cputime_expires.prof_exp =
-                       secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur);
+       cpu_limit = ACCESS_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
+       if (cpu_limit != RLIM_INFINITY) {
+               sig->cputime_expires.prof_exp = secs_to_cputime(cpu_limit);
                sig->cputimer.running = 1;
        }
 
@@ -1034,7 +1039,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 #endif
        retval = -EAGAIN;
        if (atomic_read(&p->real_cred->user->processes) >=
-                       p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
+                       task_rlimit(p, RLIMIT_NPROC)) {
                if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
                    p->real_cred->user != INIT_USER)
                        goto bad_fork_free;
index ccec774c716d82d631d291e8c8693fb3da307b55..fa034d29cf73d5730f531b7bfd98682278d077b4 100644 (file)
 #include <linux/freezer.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/sysctl.h>
 #include <linux/kdebug.h>
 #include <linux/memory.h>
 #include <linux/ftrace.h>
+#include <linux/cpu.h>
 
 #include <asm-generic/sections.h>
 #include <asm/cacheflush.h>
@@ -105,57 +107,74 @@ static struct kprobe_blackpoint kprobe_blacklist[] = {
  * stepping on the instruction on a vmalloced/kmalloced/data page
  * is a recipe for disaster
  */
-#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
-
 struct kprobe_insn_page {
        struct list_head list;
        kprobe_opcode_t *insns;         /* Page of instruction slots */
-       char slot_used[INSNS_PER_PAGE];
        int nused;
        int ngarbage;
+       char slot_used[];
+};
+
+#define KPROBE_INSN_PAGE_SIZE(slots)                   \
+       (offsetof(struct kprobe_insn_page, slot_used) + \
+        (sizeof(char) * (slots)))
+
+struct kprobe_insn_cache {
+       struct list_head pages; /* list of kprobe_insn_page */
+       size_t insn_size;       /* size of instruction slot */
+       int nr_garbage;
 };
 
+static int slots_per_page(struct kprobe_insn_cache *c)
+{
+       return PAGE_SIZE/(c->insn_size * sizeof(kprobe_opcode_t));
+}
+
 enum kprobe_slot_state {
        SLOT_CLEAN = 0,
        SLOT_DIRTY = 1,
        SLOT_USED = 2,
 };
 
-static DEFINE_MUTEX(kprobe_insn_mutex);        /* Protects kprobe_insn_pages */
-static LIST_HEAD(kprobe_insn_pages);
-static int kprobe_garbage_slots;
-static int collect_garbage_slots(void);
+static DEFINE_MUTEX(kprobe_insn_mutex);        /* Protects kprobe_insn_slots */
+static struct kprobe_insn_cache kprobe_insn_slots = {
+       .pages = LIST_HEAD_INIT(kprobe_insn_slots.pages),
+       .insn_size = MAX_INSN_SIZE,
+       .nr_garbage = 0,
+};
+static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);
 
 /**
  * __get_insn_slot() - Find a slot on an executable page for an instruction.
  * We allocate an executable page if there's no room on existing ones.
  */
-static kprobe_opcode_t __kprobes *__get_insn_slot(void)
+static kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
 {
        struct kprobe_insn_page *kip;
 
  retry:
-       list_for_each_entry(kip, &kprobe_insn_pages, list) {
-               if (kip->nused < INSNS_PER_PAGE) {
+       list_for_each_entry(kip, &c->pages, list) {
+               if (kip->nused < slots_per_page(c)) {
                        int i;
-                       for (i = 0; i < INSNS_PER_PAGE; i++) {
+                       for (i = 0; i < slots_per_page(c); i++) {
                                if (kip->slot_used[i] == SLOT_CLEAN) {
                                        kip->slot_used[i] = SLOT_USED;
                                        kip->nused++;
-                                       return kip->insns + (i * MAX_INSN_SIZE);
+                                       return kip->insns + (i * c->insn_size);
                                }
                        }
-                       /* Surprise!  No unused slots.  Fix kip->nused. */
-                       kip->nused = INSNS_PER_PAGE;
+                       /* kip->nused is broken. Fix it. */
+                       kip->nused = slots_per_page(c);
+                       WARN_ON(1);
                }
        }
 
        /* If there are any garbage slots, collect it and try again. */
-       if (kprobe_garbage_slots && collect_garbage_slots() == 0) {
+       if (c->nr_garbage && collect_garbage_slots(c) == 0)
                goto retry;
-       }
-       /* All out of space.  Need to allocate a new page. Use slot 0. */
-       kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_KERNEL);
+
+       /* All out of space.  Need to allocate a new page. */
+       kip = kmalloc(KPROBE_INSN_PAGE_SIZE(slots_per_page(c)), GFP_KERNEL);
        if (!kip)
                return NULL;
 
@@ -170,20 +189,23 @@ static kprobe_opcode_t __kprobes *__get_insn_slot(void)
                return NULL;
        }
        INIT_LIST_HEAD(&kip->list);
-       list_add(&kip->list, &kprobe_insn_pages);
-       memset(kip->slot_used, SLOT_CLEAN, INSNS_PER_PAGE);
+       memset(kip->slot_used, SLOT_CLEAN, slots_per_page(c));
        kip->slot_used[0] = SLOT_USED;
        kip->nused = 1;
        kip->ngarbage = 0;
+       list_add(&kip->list, &c->pages);
        return kip->insns;
 }
 
+
 kprobe_opcode_t __kprobes *get_insn_slot(void)
 {
-       kprobe_opcode_t *ret;
+       kprobe_opcode_t *ret = NULL;
+
        mutex_lock(&kprobe_insn_mutex);
-       ret = __get_insn_slot();
+       ret = __get_insn_slot(&kprobe_insn_slots);
        mutex_unlock(&kprobe_insn_mutex);
+
        return ret;
 }
 
@@ -199,7 +221,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
                 * so as not to have to set it up again the
                 * next time somebody inserts a probe.
                 */
-               if (!list_is_singular(&kprobe_insn_pages)) {
+               if (!list_is_singular(&kip->list)) {
                        list_del(&kip->list);
                        module_free(NULL, kip->insns);
                        kfree(kip);
@@ -209,51 +231,84 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
        return 0;
 }
 
-static int __kprobes collect_garbage_slots(void)
+static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
 {
        struct kprobe_insn_page *kip, *next;
 
        /* Ensure no-one is interrupted on the garbages */
        synchronize_sched();
 
-       list_for_each_entry_safe(kip, next, &kprobe_insn_pages, list) {
+       list_for_each_entry_safe(kip, next, &c->pages, list) {
                int i;
                if (kip->ngarbage == 0)
                        continue;
                kip->ngarbage = 0;      /* we will collect all garbages */
-               for (i = 0; i < INSNS_PER_PAGE; i++) {
+               for (i = 0; i < slots_per_page(c); i++) {
                        if (kip->slot_used[i] == SLOT_DIRTY &&
                            collect_one_slot(kip, i))
                                break;
                }
        }
-       kprobe_garbage_slots = 0;
+       c->nr_garbage = 0;
        return 0;
 }
 
-void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
+static void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
+                                      kprobe_opcode_t *slot, int dirty)
 {
        struct kprobe_insn_page *kip;
 
-       mutex_lock(&kprobe_insn_mutex);
-       list_for_each_entry(kip, &kprobe_insn_pages, list) {
-               if (kip->insns <= slot &&
-                   slot < kip->insns + (INSNS_PER_PAGE * MAX_INSN_SIZE)) {
-                       int i = (slot - kip->insns) / MAX_INSN_SIZE;
+       list_for_each_entry(kip, &c->pages, list) {
+               long idx = ((long)slot - (long)kip->insns) / c->insn_size;
+               if (idx >= 0 && idx < slots_per_page(c)) {
+                       WARN_ON(kip->slot_used[idx] != SLOT_USED);
                        if (dirty) {
-                               kip->slot_used[i] = SLOT_DIRTY;
+                               kip->slot_used[idx] = SLOT_DIRTY;
                                kip->ngarbage++;
+                               if (++c->nr_garbage > slots_per_page(c))
+                                       collect_garbage_slots(c);
                        } else
-                               collect_one_slot(kip, i);
-                       break;
+                               collect_one_slot(kip, idx);
+                       return;
                }
        }
+       /* Could not free this slot. */
+       WARN_ON(1);
+}
 
-       if (dirty && ++kprobe_garbage_slots > INSNS_PER_PAGE)
-               collect_garbage_slots();
-
+void __kprobes free_insn_slot(kprobe_opcode_t * slot, int dirty)
+{
+       mutex_lock(&kprobe_insn_mutex);
+       __free_insn_slot(&kprobe_insn_slots, slot, dirty);
        mutex_unlock(&kprobe_insn_mutex);
 }
+#ifdef CONFIG_OPTPROBES
+/* For optimized_kprobe buffer */
+static DEFINE_MUTEX(kprobe_optinsn_mutex); /* Protects kprobe_optinsn_slots */
+static struct kprobe_insn_cache kprobe_optinsn_slots = {
+       .pages = LIST_HEAD_INIT(kprobe_optinsn_slots.pages),
+       /* .insn_size is initialized later */
+       .nr_garbage = 0,
+};
+/* Get a slot for optimized_kprobe buffer */
+kprobe_opcode_t __kprobes *get_optinsn_slot(void)
+{
+       kprobe_opcode_t *ret = NULL;
+
+       mutex_lock(&kprobe_optinsn_mutex);
+       ret = __get_insn_slot(&kprobe_optinsn_slots);
+       mutex_unlock(&kprobe_optinsn_mutex);
+
+       return ret;
+}
+
+void __kprobes free_optinsn_slot(kprobe_opcode_t * slot, int dirty)
+{
+       mutex_lock(&kprobe_optinsn_mutex);
+       __free_insn_slot(&kprobe_optinsn_slots, slot, dirty);
+       mutex_unlock(&kprobe_optinsn_mutex);
+}
+#endif
 #endif
 
 /* We have preemption disabled.. so it is safe to use __ versions */
@@ -284,23 +339,401 @@ struct kprobe __kprobes *get_kprobe(void *addr)
                if (p->addr == addr)
                        return p;
        }
+
+       return NULL;
+}
+
+static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs);
+
+/* Return true if the kprobe is an aggregator */
+static inline int kprobe_aggrprobe(struct kprobe *p)
+{
+       return p->pre_handler == aggr_pre_handler;
+}
+
+/*
+ * Keep all fields in the kprobe consistent
+ */
+static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
+{
+       memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t));
+       memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn));
+}
+
+#ifdef CONFIG_OPTPROBES
+/* NOTE: change this value only with kprobe_mutex held */
+static bool kprobes_allow_optimization;
+
+/*
+ * Call all pre_handler on the list, but ignores its return value.
+ * This must be called from arch-dep optimized caller.
+ */
+void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kprobe *kp;
+
+       list_for_each_entry_rcu(kp, &p->list, list) {
+               if (kp->pre_handler && likely(!kprobe_disabled(kp))) {
+                       set_kprobe_instance(kp);
+                       kp->pre_handler(kp, regs);
+               }
+               reset_kprobe_instance();
+       }
+}
+
+/* Return true(!0) if the kprobe is ready for optimization. */
+static inline int kprobe_optready(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       if (kprobe_aggrprobe(p)) {
+               op = container_of(p, struct optimized_kprobe, kp);
+               return arch_prepared_optinsn(&op->optinsn);
+       }
+
+       return 0;
+}
+
+/*
+ * Return an optimized kprobe whose optimizing code replaces
+ * instructions including addr (exclude breakpoint).
+ */
+struct kprobe *__kprobes get_optimized_kprobe(unsigned long addr)
+{
+       int i;
+       struct kprobe *p = NULL;
+       struct optimized_kprobe *op;
+
+       /* Don't check i == 0, since that is a breakpoint case. */
+       for (i = 1; !p && i < MAX_OPTIMIZED_LENGTH; i++)
+               p = get_kprobe((void *)(addr - i));
+
+       if (p && kprobe_optready(p)) {
+               op = container_of(p, struct optimized_kprobe, kp);
+               if (arch_within_optimized_kprobe(op, addr))
+                       return p;
+       }
+
        return NULL;
 }
 
+/* Optimization staging list, protected by kprobe_mutex */
+static LIST_HEAD(optimizing_list);
+
+static void kprobe_optimizer(struct work_struct *work);
+static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer);
+#define OPTIMIZE_DELAY 5
+
+/* Kprobe jump optimizer */
+static __kprobes void kprobe_optimizer(struct work_struct *work)
+{
+       struct optimized_kprobe *op, *tmp;
+
+       /* Lock modules while optimizing kprobes */
+       mutex_lock(&module_mutex);
+       mutex_lock(&kprobe_mutex);
+       if (kprobes_all_disarmed || !kprobes_allow_optimization)
+               goto end;
+
+       /*
+        * Wait for quiesence period to ensure all running interrupts
+        * are done. Because optprobe may modify multiple instructions
+        * there is a chance that Nth instruction is interrupted. In that
+        * case, running interrupt can return to 2nd-Nth byte of jump
+        * instruction. This wait is for avoiding it.
+        */
+       synchronize_sched();
+
+       /*
+        * The optimization/unoptimization refers online_cpus via
+        * stop_machine() and cpu-hotplug modifies online_cpus.
+        * And same time, text_mutex will be held in cpu-hotplug and here.
+        * This combination can cause a deadlock (cpu-hotplug try to lock
+        * text_mutex but stop_machine can not be done because online_cpus
+        * has been changed)
+        * To avoid this deadlock, we need to call get_online_cpus()
+        * for preventing cpu-hotplug outside of text_mutex locking.
+        */
+       get_online_cpus();
+       mutex_lock(&text_mutex);
+       list_for_each_entry_safe(op, tmp, &optimizing_list, list) {
+               WARN_ON(kprobe_disabled(&op->kp));
+               if (arch_optimize_kprobe(op) < 0)
+                       op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+               list_del_init(&op->list);
+       }
+       mutex_unlock(&text_mutex);
+       put_online_cpus();
+end:
+       mutex_unlock(&kprobe_mutex);
+       mutex_unlock(&module_mutex);
+}
+
+/* Optimize kprobe if p is ready to be optimized */
+static __kprobes void optimize_kprobe(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       /* Check if the kprobe is disabled or not ready for optimization. */
+       if (!kprobe_optready(p) || !kprobes_allow_optimization ||
+           (kprobe_disabled(p) || kprobes_all_disarmed))
+               return;
+
+       /* Both of break_handler and post_handler are not supported. */
+       if (p->break_handler || p->post_handler)
+               return;
+
+       op = container_of(p, struct optimized_kprobe, kp);
+
+       /* Check there is no other kprobes at the optimized instructions */
+       if (arch_check_optimized_kprobe(op) < 0)
+               return;
+
+       /* Check if it is already optimized. */
+       if (op->kp.flags & KPROBE_FLAG_OPTIMIZED)
+               return;
+
+       op->kp.flags |= KPROBE_FLAG_OPTIMIZED;
+       list_add(&op->list, &optimizing_list);
+       if (!delayed_work_pending(&optimizing_work))
+               schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
+}
+
+/* Unoptimize a kprobe if p is optimized */
+static __kprobes void unoptimize_kprobe(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       if ((p->flags & KPROBE_FLAG_OPTIMIZED) && kprobe_aggrprobe(p)) {
+               op = container_of(p, struct optimized_kprobe, kp);
+               if (!list_empty(&op->list))
+                       /* Dequeue from the optimization queue */
+                       list_del_init(&op->list);
+               else
+                       /* Replace jump with break */
+                       arch_unoptimize_kprobe(op);
+               op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+       }
+}
+
+/* Remove optimized instructions */
+static void __kprobes kill_optimized_kprobe(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       op = container_of(p, struct optimized_kprobe, kp);
+       if (!list_empty(&op->list)) {
+               /* Dequeue from the optimization queue */
+               list_del_init(&op->list);
+               op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+       }
+       /* Don't unoptimize, because the target code will be freed. */
+       arch_remove_optimized_kprobe(op);
+}
+
+/* Try to prepare optimized instructions */
+static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       op = container_of(p, struct optimized_kprobe, kp);
+       arch_prepare_optimized_kprobe(op);
+}
+
+/* Free optimized instructions and optimized_kprobe */
+static __kprobes void free_aggr_kprobe(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       op = container_of(p, struct optimized_kprobe, kp);
+       arch_remove_optimized_kprobe(op);
+       kfree(op);
+}
+
+/* Allocate new optimized_kprobe and try to prepare optimized instructions */
+static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+{
+       struct optimized_kprobe *op;
+
+       op = kzalloc(sizeof(struct optimized_kprobe), GFP_KERNEL);
+       if (!op)
+               return NULL;
+
+       INIT_LIST_HEAD(&op->list);
+       op->kp.addr = p->addr;
+       arch_prepare_optimized_kprobe(op);
+
+       return &op->kp;
+}
+
+static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p);
+
+/*
+ * Prepare an optimized_kprobe and optimize it
+ * NOTE: p must be a normal registered kprobe
+ */
+static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
+{
+       struct kprobe *ap;
+       struct optimized_kprobe *op;
+
+       ap = alloc_aggr_kprobe(p);
+       if (!ap)
+               return;
+
+       op = container_of(ap, struct optimized_kprobe, kp);
+       if (!arch_prepared_optinsn(&op->optinsn)) {
+               /* If failed to setup optimizing, fallback to kprobe */
+               free_aggr_kprobe(ap);
+               return;
+       }
+
+       init_aggr_kprobe(ap, p);
+       optimize_kprobe(ap);
+}
+
+#ifdef CONFIG_SYSCTL
+static void __kprobes optimize_all_kprobes(void)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct kprobe *p;
+       unsigned int i;
+
+       /* If optimization is already allowed, just return */
+       if (kprobes_allow_optimization)
+               return;
+
+       kprobes_allow_optimization = true;
+       mutex_lock(&text_mutex);
+       for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
+               head = &kprobe_table[i];
+               hlist_for_each_entry_rcu(p, node, head, hlist)
+                       if (!kprobe_disabled(p))
+                               optimize_kprobe(p);
+       }
+       mutex_unlock(&text_mutex);
+       printk(KERN_INFO "Kprobes globally optimized\n");
+}
+
+static void __kprobes unoptimize_all_kprobes(void)
+{
+       struct hlist_head *head;
+       struct hlist_node *node;
+       struct kprobe *p;
+       unsigned int i;
+
+       /* If optimization is already prohibited, just return */
+       if (!kprobes_allow_optimization)
+               return;
+
+       kprobes_allow_optimization = false;
+       printk(KERN_INFO "Kprobes globally unoptimized\n");
+       get_online_cpus();      /* For avoiding text_mutex deadlock */
+       mutex_lock(&text_mutex);
+       for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
+               head = &kprobe_table[i];
+               hlist_for_each_entry_rcu(p, node, head, hlist) {
+                       if (!kprobe_disabled(p))
+                               unoptimize_kprobe(p);
+               }
+       }
+
+       mutex_unlock(&text_mutex);
+       put_online_cpus();
+       /* Allow all currently running kprobes to complete */
+       synchronize_sched();
+}
+
+int sysctl_kprobes_optimization;
+int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
+                                     void __user *buffer, size_t *length,
+                                     loff_t *ppos)
+{
+       int ret;
+
+       mutex_lock(&kprobe_mutex);
+       sysctl_kprobes_optimization = kprobes_allow_optimization ? 1 : 0;
+       ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
+
+       if (sysctl_kprobes_optimization)
+               optimize_all_kprobes();
+       else
+               unoptimize_all_kprobes();
+       mutex_unlock(&kprobe_mutex);
+
+       return ret;
+}
+#endif /* CONFIG_SYSCTL */
+
+static void __kprobes __arm_kprobe(struct kprobe *p)
+{
+       struct kprobe *old_p;
+
+       /* Check collision with other optimized kprobes */
+       old_p = get_optimized_kprobe((unsigned long)p->addr);
+       if (unlikely(old_p))
+               unoptimize_kprobe(old_p); /* Fallback to unoptimized kprobe */
+
+       arch_arm_kprobe(p);
+       optimize_kprobe(p);     /* Try to optimize (add kprobe to a list) */
+}
+
+static void __kprobes __disarm_kprobe(struct kprobe *p)
+{
+       struct kprobe *old_p;
+
+       unoptimize_kprobe(p);   /* Try to unoptimize */
+       arch_disarm_kprobe(p);
+
+       /* If another kprobe was blocked, optimize it. */
+       old_p = get_optimized_kprobe((unsigned long)p->addr);
+       if (unlikely(old_p))
+               optimize_kprobe(old_p);
+}
+
+#else /* !CONFIG_OPTPROBES */
+
+#define optimize_kprobe(p)                     do {} while (0)
+#define unoptimize_kprobe(p)                   do {} while (0)
+#define kill_optimized_kprobe(p)               do {} while (0)
+#define prepare_optimized_kprobe(p)            do {} while (0)
+#define try_to_optimize_kprobe(p)              do {} while (0)
+#define __arm_kprobe(p)                                arch_arm_kprobe(p)
+#define __disarm_kprobe(p)                     arch_disarm_kprobe(p)
+
+static __kprobes void free_aggr_kprobe(struct kprobe *p)
+{
+       kfree(p);
+}
+
+static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+{
+       return kzalloc(sizeof(struct kprobe), GFP_KERNEL);
+}
+#endif /* CONFIG_OPTPROBES */
+
 /* Arm a kprobe with text_mutex */
 static void __kprobes arm_kprobe(struct kprobe *kp)
 {
+       /*
+        * Here, since __arm_kprobe() doesn't use stop_machine(),
+        * this doesn't cause deadlock on text_mutex. So, we don't
+        * need get_online_cpus().
+        */
        mutex_lock(&text_mutex);
-       arch_arm_kprobe(kp);
+       __arm_kprobe(kp);
        mutex_unlock(&text_mutex);
 }
 
 /* Disarm a kprobe with text_mutex */
 static void __kprobes disarm_kprobe(struct kprobe *kp)
 {
+       get_online_cpus();      /* For avoiding text_mutex deadlock */
        mutex_lock(&text_mutex);
-       arch_disarm_kprobe(kp);
+       __disarm_kprobe(kp);
        mutex_unlock(&text_mutex);
+       put_online_cpus();
 }
 
 /*
@@ -369,7 +802,7 @@ static int __kprobes aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
 void __kprobes kprobes_inc_nmissed_count(struct kprobe *p)
 {
        struct kprobe *kp;
-       if (p->pre_handler != aggr_pre_handler) {
+       if (!kprobe_aggrprobe(p)) {
                p->nmissed++;
        } else {
                list_for_each_entry_rcu(kp, &p->list, list)
@@ -492,15 +925,6 @@ static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
        free_rp_inst(rp);
 }
 
-/*
- * Keep all fields in the kprobe consistent
- */
-static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
-{
-       memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t));
-       memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn));
-}
-
 /*
 * Add the new probe to ap->list. Fail if this is the
 * second jprobe at the address - two jprobes can't coexist
@@ -508,6 +932,10 @@ static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
 static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
 {
        BUG_ON(kprobe_gone(ap) || kprobe_gone(p));
+
+       if (p->break_handler || p->post_handler)
+               unoptimize_kprobe(ap);  /* Fall back to normal kprobe */
+
        if (p->break_handler) {
                if (ap->break_handler)
                        return -EEXIST;
@@ -522,7 +950,7 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
                ap->flags &= ~KPROBE_FLAG_DISABLED;
                if (!kprobes_all_disarmed)
                        /* Arm the breakpoint again. */
-                       arm_kprobe(ap);
+                       __arm_kprobe(ap);
        }
        return 0;
 }
@@ -531,12 +959,13 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
  * Fill in the required fields of the "manager kprobe". Replace the
  * earlier kprobe in the hlist with the manager kprobe
  */
-static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
+static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
 {
+       /* Copy p's insn slot to ap */
        copy_kprobe(p, ap);
        flush_insn_slot(ap);
        ap->addr = p->addr;
-       ap->flags = p->flags;
+       ap->flags = p->flags & ~KPROBE_FLAG_OPTIMIZED;
        ap->pre_handler = aggr_pre_handler;
        ap->fault_handler = aggr_fault_handler;
        /* We don't care the kprobe which has gone. */
@@ -546,8 +975,9 @@ static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
                ap->break_handler = aggr_break_handler;
 
        INIT_LIST_HEAD(&ap->list);
-       list_add_rcu(&p->list, &ap->list);
+       INIT_HLIST_NODE(&ap->hlist);
 
+       list_add_rcu(&p->list, &ap->list);
        hlist_replace_rcu(&p->hlist, &ap->hlist);
 }
 
@@ -561,12 +991,12 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
        int ret = 0;
        struct kprobe *ap = old_p;
 
-       if (old_p->pre_handler != aggr_pre_handler) {
-               /* If old_p is not an aggr_probe, create new aggr_kprobe. */
-               ap = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
+       if (!kprobe_aggrprobe(old_p)) {
+               /* If old_p is not an aggr_kprobe, create new aggr_kprobe. */
+               ap = alloc_aggr_kprobe(old_p);
                if (!ap)
                        return -ENOMEM;
-               add_aggr_kprobe(ap, old_p);
+               init_aggr_kprobe(ap, old_p);
        }
 
        if (kprobe_gone(ap)) {
@@ -585,6 +1015,9 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
                         */
                        return ret;
 
+               /* Prepare optimized instructions if possible. */
+               prepare_optimized_kprobe(ap);
+
                /*
                 * Clear gone flag to prevent allocating new slot again, and
                 * set disabled flag because it is not armed yet.
@@ -593,6 +1026,7 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
                            | KPROBE_FLAG_DISABLED;
        }
 
+       /* Copy ap's insn slot to p */
        copy_kprobe(ap, p);
        return add_new_kprobe(ap, p);
 }
@@ -743,27 +1177,34 @@ int __kprobes register_kprobe(struct kprobe *p)
        p->nmissed = 0;
        INIT_LIST_HEAD(&p->list);
        mutex_lock(&kprobe_mutex);
+
+       get_online_cpus();      /* For avoiding text_mutex deadlock. */
+       mutex_lock(&text_mutex);
+
        old_p = get_kprobe(p->addr);
        if (old_p) {
+               /* Since this may unoptimize old_p, locking text_mutex. */
                ret = register_aggr_kprobe(old_p, p);
                goto out;
        }
 
-       mutex_lock(&text_mutex);
        ret = arch_prepare_kprobe(p);
        if (ret)
-               goto out_unlock_text;
+               goto out;
 
        INIT_HLIST_NODE(&p->hlist);
        hlist_add_head_rcu(&p->hlist,
                       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
 
        if (!kprobes_all_disarmed && !kprobe_disabled(p))
-               arch_arm_kprobe(p);
+               __arm_kprobe(p);
+
+       /* Try to optimize kprobe */
+       try_to_optimize_kprobe(p);
 
-out_unlock_text:
-       mutex_unlock(&text_mutex);
 out:
+       mutex_unlock(&text_mutex);
+       put_online_cpus();
        mutex_unlock(&kprobe_mutex);
 
        if (probed_mod)
@@ -785,7 +1226,7 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
                return -EINVAL;
 
        if (old_p == p ||
-           (old_p->pre_handler == aggr_pre_handler &&
+           (kprobe_aggrprobe(old_p) &&
             list_is_singular(&old_p->list))) {
                /*
                 * Only probe on the hash list. Disarm only if kprobes are
@@ -793,7 +1234,7 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
                 * already have been removed. We save on flushing icache.
                 */
                if (!kprobes_all_disarmed && !kprobe_disabled(old_p))
-                       disarm_kprobe(p);
+                       disarm_kprobe(old_p);
                hlist_del_rcu(&old_p->hlist);
        } else {
                if (p->break_handler && !kprobe_gone(p))
@@ -809,8 +1250,13 @@ noclean:
                list_del_rcu(&p->list);
                if (!kprobe_disabled(old_p)) {
                        try_to_disable_aggr_kprobe(old_p);
-                       if (!kprobes_all_disarmed && kprobe_disabled(old_p))
-                               disarm_kprobe(old_p);
+                       if (!kprobes_all_disarmed) {
+                               if (kprobe_disabled(old_p))
+                                       disarm_kprobe(old_p);
+                               else
+                                       /* Try to optimize this probe again */
+                                       optimize_kprobe(old_p);
+                       }
                }
        }
        return 0;
@@ -827,7 +1273,7 @@ static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
                old_p = list_entry(p->list.next, struct kprobe, list);
                list_del(&p->list);
                arch_remove_kprobe(old_p);
-               kfree(old_p);
+               free_aggr_kprobe(old_p);
        }
 }
 
@@ -1123,7 +1569,7 @@ static void __kprobes kill_kprobe(struct kprobe *p)
        struct kprobe *kp;
 
        p->flags |= KPROBE_FLAG_GONE;
-       if (p->pre_handler == aggr_pre_handler) {
+       if (kprobe_aggrprobe(p)) {
                /*
                 * If this is an aggr_kprobe, we have to list all the
                 * chained probes and mark them GONE.
@@ -1132,6 +1578,7 @@ static void __kprobes kill_kprobe(struct kprobe *p)
                        kp->flags |= KPROBE_FLAG_GONE;
                p->post_handler = NULL;
                p->break_handler = NULL;
+               kill_optimized_kprobe(p);
        }
        /*
         * Here, we can remove insn_slot safely, because no thread calls
@@ -1241,6 +1688,15 @@ static int __init init_kprobes(void)
                }
        }
 
+#if defined(CONFIG_OPTPROBES)
+#if defined(__ARCH_WANT_KPROBES_INSN_SLOT)
+       /* Init kprobe_optinsn_slots */
+       kprobe_optinsn_slots.insn_size = MAX_OPTINSN_SIZE;
+#endif
+       /* By default, kprobes can be optimized */
+       kprobes_allow_optimization = true;
+#endif
+
        /* By default, kprobes are armed */
        kprobes_all_disarmed = false;
 
@@ -1259,7 +1715,7 @@ static int __init init_kprobes(void)
 
 #ifdef CONFIG_DEBUG_FS
 static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
-               const char *sym, int offset,char *modname)
+               const char *sym, int offset, char *modname, struct kprobe *pp)
 {
        char *kprobe_type;
 
@@ -1269,19 +1725,21 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
                kprobe_type = "j";
        else
                kprobe_type = "k";
+
        if (sym)
-               seq_printf(pi, "%p  %s  %s+0x%x  %s %s%s\n",
+               seq_printf(pi, "%p  %s  %s+0x%x  %s ",
                        p->addr, kprobe_type, sym, offset,
-                       (modname ? modname : " "),
-                       (kprobe_gone(p) ? "[GONE]" : ""),
-                       ((kprobe_disabled(p) && !kprobe_gone(p)) ?
-                        "[DISABLED]" : ""));
+                       (modname ? modname : " "));
        else
-               seq_printf(pi, "%p  %s  %p %s%s\n",
-                       p->addr, kprobe_type, p->addr,
-                       (kprobe_gone(p) ? "[GONE]" : ""),
-                       ((kprobe_disabled(p) && !kprobe_gone(p)) ?
-                        "[DISABLED]" : ""));
+               seq_printf(pi, "%p  %s  %p ",
+                       p->addr, kprobe_type, p->addr);
+
+       if (!pp)
+               pp = p;
+       seq_printf(pi, "%s%s%s\n",
+               (kprobe_gone(p) ? "[GONE]" : ""),
+               ((kprobe_disabled(p) && !kprobe_gone(p)) ?  "[DISABLED]" : ""),
+               (kprobe_optimized(pp) ? "[OPTIMIZED]" : ""));
 }
 
 static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
@@ -1317,11 +1775,11 @@ static int __kprobes show_kprobe_addr(struct seq_file *pi, void *v)
        hlist_for_each_entry_rcu(p, node, head, hlist) {
                sym = kallsyms_lookup((unsigned long)p->addr, NULL,
                                        &offset, &modname, namebuf);
-               if (p->pre_handler == aggr_pre_handler) {
+               if (kprobe_aggrprobe(p)) {
                        list_for_each_entry_rcu(kp, &p->list, list)
-                               report_probe(pi, kp, sym, offset, modname);
+                               report_probe(pi, kp, sym, offset, modname, p);
                } else
-                       report_probe(pi, p, sym, offset, modname);
+                       report_probe(pi, p, sym, offset, modname, NULL);
        }
        preempt_enable();
        return 0;
@@ -1399,12 +1857,13 @@ int __kprobes enable_kprobe(struct kprobe *kp)
                goto out;
        }
 
-       if (!kprobes_all_disarmed && kprobe_disabled(p))
-               arm_kprobe(p);
-
-       p->flags &= ~KPROBE_FLAG_DISABLED;
        if (p != kp)
                kp->flags &= ~KPROBE_FLAG_DISABLED;
+
+       if (!kprobes_all_disarmed && kprobe_disabled(p)) {
+               p->flags &= ~KPROBE_FLAG_DISABLED;
+               arm_kprobe(p);
+       }
 out:
        mutex_unlock(&kprobe_mutex);
        return ret;
@@ -1424,12 +1883,13 @@ static void __kprobes arm_all_kprobes(void)
        if (!kprobes_all_disarmed)
                goto already_enabled;
 
+       /* Arming kprobes doesn't optimize kprobe itself */
        mutex_lock(&text_mutex);
        for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
                head = &kprobe_table[i];
                hlist_for_each_entry_rcu(p, node, head, hlist)
                        if (!kprobe_disabled(p))
-                               arch_arm_kprobe(p);
+                               __arm_kprobe(p);
        }
        mutex_unlock(&text_mutex);
 
@@ -1456,16 +1916,23 @@ static void __kprobes disarm_all_kprobes(void)
 
        kprobes_all_disarmed = true;
        printk(KERN_INFO "Kprobes globally disabled\n");
+
+       /*
+        * Here we call get_online_cpus() for avoiding text_mutex deadlock,
+        * because disarming may also unoptimize kprobes.
+        */
+       get_online_cpus();
        mutex_lock(&text_mutex);
        for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
                head = &kprobe_table[i];
                hlist_for_each_entry_rcu(p, node, head, hlist) {
                        if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p))
-                               arch_disarm_kprobe(p);
+                               __disarm_kprobe(p);
                }
        }
 
        mutex_unlock(&text_mutex);
+       put_online_cpus();
        mutex_unlock(&kprobe_mutex);
        /* Allow all currently running kprobes to complete */
        synchronize_sched();
index 6f9bcb8313d604f62bceb54d21c1e2eeb0f48211..93caf65ff57c80cf52ce215e51c65694060d95af 100644 (file)
@@ -642,6 +642,9 @@ struct padata_instance *padata_alloc(const struct cpumask *cpumask,
        if (!pd)
                goto err_free_inst;
 
+       if (!alloc_cpumask_var(&pinst->cpumask, GFP_KERNEL))
+               goto err_free_pd;
+
        rcu_assign_pointer(pinst->pd, pd);
 
        pinst->wq = wq;
@@ -654,12 +657,14 @@ struct padata_instance *padata_alloc(const struct cpumask *cpumask,
        pinst->cpu_notifier.priority = 0;
        err = register_hotcpu_notifier(&pinst->cpu_notifier);
        if (err)
-               goto err_free_pd;
+               goto err_free_cpumask;
 
        mutex_init(&pinst->lock);
 
        return pinst;
 
+err_free_cpumask:
+       free_cpumask_var(pinst->cpumask);
 err_free_pd:
        padata_free_pd(pd);
 err_free_inst:
@@ -685,6 +690,7 @@ void padata_free(struct padata_instance *pinst)
 
        unregister_hotcpu_notifier(&pinst->cpu_notifier);
        padata_free_pd(pinst->pd);
+       free_cpumask_var(pinst->cpumask);
        kfree(pinst);
 }
 EXPORT_SYMBOL(padata_free);
index c787333282b8c4e82a5eb125ca99efd480a94f7a..13d966b4c14a26f381a563aafe8721bc278a941d 100644 (file)
@@ -36,15 +36,36 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
 
 EXPORT_SYMBOL(panic_notifier_list);
 
-static long no_blink(long time)
-{
-       return 0;
-}
-
 /* Returns how long it waited in ms */
 long (*panic_blink)(long time);
 EXPORT_SYMBOL(panic_blink);
 
+static void panic_blink_one_second(void)
+{
+       static long i = 0, end;
+
+       if (panic_blink) {
+               end = i + MSEC_PER_SEC;
+
+               while (i < end) {
+                       i += panic_blink(i);
+                       mdelay(1);
+                       i++;
+               }
+       } else {
+               /*
+                * When running under a hypervisor a small mdelay may get
+                * rounded up to the hypervisor timeslice. For example, with
+                * a 1ms in 10ms hypervisor timeslice we might inflate a
+                * mdelay(1) loop by 10x.
+                *
+                * If we have nothing to blink, spin on 1 second calls to
+                * mdelay to avoid this.
+                */
+               mdelay(MSEC_PER_SEC);
+       }
+}
+
 /**
  *     panic - halt the system
  *     @fmt: The text string to print
@@ -95,9 +116,6 @@ NORET_TYPE void panic(const char * fmt, ...)
 
        bust_spinlocks(0);
 
-       if (!panic_blink)
-               panic_blink = no_blink;
-
        if (panic_timeout > 0) {
                /*
                 * Delay timeout seconds before rebooting the machine.
@@ -105,11 +123,9 @@ NORET_TYPE void panic(const char * fmt, ...)
                 */
                printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout);
 
-               for (i = 0; i < panic_timeout*1000; ) {
+               for (i = 0; i < panic_timeout; i++) {
                        touch_nmi_watchdog();
-                       i += panic_blink(i);
-                       mdelay(1);
-                       i++;
+                       panic_blink_one_second();
                }
                /*
                 * This will not be a clean reboot, with everything
@@ -135,11 +151,9 @@ NORET_TYPE void panic(const char * fmt, ...)
        }
 #endif
        local_irq_enable();
-       for (i = 0; ; ) {
+       while (1) {
                touch_softlockup_watchdog();
-               i += panic_blink(i);
-               mdelay(1);
-               i++;
+               panic_blink_one_second();
        }
 }
 
index cf1b691831275f85247ebd93628b8015fac1c70d..8d95f5451b227d17cb40e1290dea72b028ca0ec9 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/ctype.h>
-#include <linux/string.h>
 
 #if 0
 #define DEBUGP printk
index a661e7991865bdeb22d6631b01a96d8a4b6c3047..8e352c756ba742cda4dfa22bdb9954ed675d305e 100644 (file)
@@ -2610,7 +2610,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
        if (user_locked > user_lock_limit)
                extra = user_locked - user_lock_limit;
 
-       lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+       lock_limit = rlimit(RLIMIT_MEMLOCK);
        lock_limit >>= PAGE_SHIFT;
        locked = vma->vm_mm->locked_vm + extra;
 
index b08e697cd83f0944747d5624698d356cf0288090..86b296943e5f274c2b4965a73275661a4c6c727e 100644 (file)
@@ -376,7 +376,7 @@ struct task_struct *pid_task(struct pid *pid, enum pid_type type)
 EXPORT_SYMBOL(pid_task);
 
 /*
- * Must be called under rcu_read_lock() or with tasklist_lock read-held.
+ * Must be called under rcu_read_lock().
  */
 struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns)
 {
index 438ff4523513b945962e50a98eb8d4e2fbb28f5d..1a22dfd42df9b4089e9630f2635a425a26d4959b 100644 (file)
@@ -982,6 +982,7 @@ static void check_thread_timers(struct task_struct *tsk,
        int maxfire;
        struct list_head *timers = tsk->cpu_timers;
        struct signal_struct *const sig = tsk->signal;
+       unsigned long soft;
 
        maxfire = 20;
        tsk->cputime_expires.prof_exp = cputime_zero;
@@ -1030,9 +1031,10 @@ static void check_thread_timers(struct task_struct *tsk,
        /*
         * Check for the special case thread timers.
         */
-       if (sig->rlim[RLIMIT_RTTIME].rlim_cur != RLIM_INFINITY) {
-               unsigned long hard = sig->rlim[RLIMIT_RTTIME].rlim_max;
-               unsigned long *soft = &sig->rlim[RLIMIT_RTTIME].rlim_cur;
+       soft = ACCESS_ONCE(sig->rlim[RLIMIT_RTTIME].rlim_cur);
+       if (soft != RLIM_INFINITY) {
+               unsigned long hard =
+                       ACCESS_ONCE(sig->rlim[RLIMIT_RTTIME].rlim_max);
 
                if (hard != RLIM_INFINITY &&
                    tsk->rt.timeout > DIV_ROUND_UP(hard, USEC_PER_SEC/HZ)) {
@@ -1043,14 +1045,13 @@ static void check_thread_timers(struct task_struct *tsk,
                        __group_send_sig_info(SIGKILL, SEND_SIG_PRIV, tsk);
                        return;
                }
-               if (tsk->rt.timeout > DIV_ROUND_UP(*soft, USEC_PER_SEC/HZ)) {
+               if (tsk->rt.timeout > DIV_ROUND_UP(soft, USEC_PER_SEC/HZ)) {
                        /*
                         * At the soft limit, send a SIGXCPU every second.
                         */
-                       if (sig->rlim[RLIMIT_RTTIME].rlim_cur
-                           < sig->rlim[RLIMIT_RTTIME].rlim_max) {
-                               sig->rlim[RLIMIT_RTTIME].rlim_cur +=
-                                                               USEC_PER_SEC;
+                       if (soft < hard) {
+                               soft += USEC_PER_SEC;
+                               sig->rlim[RLIMIT_RTTIME].rlim_cur = soft;
                        }
                        printk(KERN_INFO
                                "RT Watchdog Timeout: %s[%d]\n",
@@ -1121,6 +1122,7 @@ static void check_process_timers(struct task_struct *tsk,
        unsigned long long sum_sched_runtime, sched_expires;
        struct list_head *timers = sig->cpu_timers;
        struct task_cputime cputime;
+       unsigned long soft;
 
        /*
         * Don't sample the current process CPU clocks if there are no timers.
@@ -1193,11 +1195,13 @@ static void check_process_timers(struct task_struct *tsk,
                         SIGPROF);
        check_cpu_itimer(tsk, &sig->it[CPUCLOCK_VIRT], &virt_expires, utime,
                         SIGVTALRM);
-
-       if (sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
+       soft = ACCESS_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
+       if (soft != RLIM_INFINITY) {
                unsigned long psecs = cputime_to_secs(ptime);
+               unsigned long hard =
+                       ACCESS_ONCE(sig->rlim[RLIMIT_CPU].rlim_max);
                cputime_t x;
-               if (psecs >= sig->rlim[RLIMIT_CPU].rlim_max) {
+               if (psecs >= hard) {
                        /*
                         * At the hard limit, we just die.
                         * No need to calculate anything else now.
@@ -1205,17 +1209,17 @@ static void check_process_timers(struct task_struct *tsk,
                        __group_send_sig_info(SIGKILL, SEND_SIG_PRIV, tsk);
                        return;
                }
-               if (psecs >= sig->rlim[RLIMIT_CPU].rlim_cur) {
+               if (psecs >= soft) {
                        /*
                         * At the soft limit, send a SIGXCPU every second.
                         */
                        __group_send_sig_info(SIGXCPU, SEND_SIG_PRIV, tsk);
-                       if (sig->rlim[RLIMIT_CPU].rlim_cur
-                           < sig->rlim[RLIMIT_CPU].rlim_max) {
-                               sig->rlim[RLIMIT_CPU].rlim_cur++;
+                       if (soft < hard) {
+                               soft++;
+                               sig->rlim[RLIMIT_CPU].rlim_cur = soft;
                        }
                }
-               x = secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur);
+               x = secs_to_cputime(soft);
                if (cputime_eq(prof_expires, cputime_zero) ||
                    cputime_lt(x, prof_expires)) {
                        prof_expires = x;
index bbfe472d7524245c0edb87f99c2af7f676ff24f2..da5288ec239286d9c2ae3f62b75a32ff3ed60f8b 100644 (file)
@@ -323,6 +323,7 @@ static int create_image(int platform_mode)
 int hibernation_snapshot(int platform_mode)
 {
        int error;
+       gfp_t saved_mask;
 
        error = platform_begin(platform_mode);
        if (error)
@@ -334,6 +335,7 @@ int hibernation_snapshot(int platform_mode)
                goto Close;
 
        suspend_console();
+       saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
        error = dpm_suspend_start(PMSG_FREEZE);
        if (error)
                goto Recover_platform;
@@ -351,6 +353,7 @@ int hibernation_snapshot(int platform_mode)
 
        dpm_resume_end(in_suspend ?
                (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
+       set_gfp_allowed_mask(saved_mask);
        resume_console();
  Close:
        platform_end(platform_mode);
@@ -445,14 +448,17 @@ static int resume_target_kernel(bool platform_mode)
 int hibernation_restore(int platform_mode)
 {
        int error;
+       gfp_t saved_mask;
 
        pm_prepare_console();
        suspend_console();
+       saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
        error = dpm_suspend_start(PMSG_QUIESCE);
        if (!error) {
                error = resume_target_kernel(platform_mode);
                dpm_resume_end(PMSG_RECOVER);
        }
+       set_gfp_allowed_mask(saved_mask);
        resume_console();
        pm_restore_console();
        return error;
@@ -466,6 +472,7 @@ int hibernation_restore(int platform_mode)
 int hibernation_platform_enter(void)
 {
        int error;
+       gfp_t saved_mask;
 
        if (!hibernation_ops)
                return -ENOSYS;
@@ -481,6 +488,7 @@ int hibernation_platform_enter(void)
 
        entering_platform_hibernation = true;
        suspend_console();
+       saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
        error = dpm_suspend_start(PMSG_HIBERNATE);
        if (error) {
                if (hibernation_ops->recover)
@@ -518,6 +526,7 @@ int hibernation_platform_enter(void)
  Resume_devices:
        entering_platform_hibernation = false;
        dpm_resume_end(PMSG_RESTORE);
+       set_gfp_allowed_mask(saved_mask);
        resume_console();
 
  Close:
index 6f10dfc2d3e9f79a0a57cd73b8a70ad2cc90d22b..44cce10b582dc06c4d0d326b95fe25631e49f251 100644 (file)
@@ -189,6 +189,7 @@ static int suspend_enter(suspend_state_t state)
 int suspend_devices_and_enter(suspend_state_t state)
 {
        int error;
+       gfp_t saved_mask;
 
        if (!suspend_ops)
                return -ENOSYS;
@@ -199,6 +200,7 @@ int suspend_devices_and_enter(suspend_state_t state)
                        goto Close;
        }
        suspend_console();
+       saved_mask = clear_gfp_allowed_mask(GFP_IOFS);
        suspend_test_start();
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
@@ -215,6 +217,7 @@ int suspend_devices_and_enter(suspend_state_t state)
        suspend_test_start();
        dpm_resume_end(PMSG_RESUME);
        suspend_test_finish("resume devices");
+       set_gfp_allowed_mask(saved_mask);
        resume_console();
  Close:
        if (suspend_ops->end)
index 40674122ecf23aed335a67d88ff4444de8970e9c..75077ad0b5378afd3fa89aa9f0199c296a8c0cd7 100644 (file)
@@ -70,8 +70,6 @@ int console_printk[4] = {
        DEFAULT_CONSOLE_LOGLEVEL,       /* default_console_loglevel */
 };
 
-static int saved_console_loglevel = -1;
-
 /*
  * Low level drivers may need that to know if they can schedule in
  * their unblank() callback or not. So let's export it.
@@ -146,6 +144,7 @@ static char __log_buf[__LOG_BUF_LEN];
 static char *log_buf = __log_buf;
 static int log_buf_len = __LOG_BUF_LEN;
 static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
+static int saved_console_loglevel = -1;
 
 #ifdef CONFIG_KEXEC
 /*
index c705a41b4ba396082e719c1b7fef9539742837ab..3d97f2821611edb54b8028d4d9e4663b2f12475c 100644 (file)
@@ -1215,14 +1215,14 @@ static void relay_page_release(struct splice_pipe_desc *spd, unsigned int i)
 /*
  *     subbuf_splice_actor - splice up to one subbuf's worth of data
  */
-static int subbuf_splice_actor(struct file *in,
+static ssize_t subbuf_splice_actor(struct file *in,
                               loff_t *ppos,
                               struct pipe_inode_info *pipe,
                               size_t len,
                               unsigned int flags,
                               int *nonpad_ret)
 {
-       unsigned int pidx, poff, total_len, subbuf_pages, nr_pages, ret;
+       unsigned int pidx, poff, total_len, subbuf_pages, nr_pages;
        struct rchan_buf *rbuf = in->private_data;
        unsigned int subbuf_size = rbuf->chan->subbuf_size;
        uint64_t pos = (uint64_t) *ppos;
@@ -1241,6 +1241,7 @@ static int subbuf_splice_actor(struct file *in,
                .ops = &relay_pipe_buf_ops,
                .spd_release = relay_page_release,
        };
+       ssize_t ret;
 
        if (rbuf->subbufs_produced == rbuf->subbufs_consumed)
                return 0;
index abb36b16b93b7ab3984d19980a3cbb494e823632..b47ceeec1a912054e63e947f8a334f0152cdcceb 100644 (file)
@@ -4353,7 +4353,7 @@ int can_nice(const struct task_struct *p, const int nice)
        /* convert nice value [19,-20] to rlimit style value [1,40] */
        int nice_rlim = 20 - nice;
 
-       return (nice_rlim <= p->signal->rlim[RLIMIT_NICE].rlim_cur ||
+       return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) ||
                capable(CAP_SYS_NICE));
 }
 
@@ -4530,7 +4530,7 @@ recheck:
 
                        if (!lock_task_sighand(p, &flags))
                                return -ESRCH;
-                       rlim_rtprio = p->signal->rlim[RLIMIT_RTPRIO].rlim_cur;
+                       rlim_rtprio = task_rlimit(p, RLIMIT_RTPRIO);
                        unlock_task_sighand(p, &flags);
 
                        /* can't set/change the rt policy */
index eeb3506c4834e167d531c5bf62409ae9322c450f..82095bf2099f7c5e535757ad655bb9a68e1f290d 100644 (file)
@@ -47,7 +47,7 @@ static int convert_prio(int prio)
 }
 
 #define for_each_cpupri_active(array, idx)                    \
-       for_each_bit(idx, array, CPUPRI_NR_PRIORITIES)
+       for_each_set_bit(idx, array, CPUPRI_NR_PRIORITIES)
 
 /**
  * cpupri_find - find the best (lowest-pri) CPU in the system
index bf3e38fdbe6dc6ca0160d7f408881525928da21a..5a6ed1f0990a5cd5f128d4f61cd8522a40ff3466 100644 (file)
@@ -1662,8 +1662,9 @@ static void watchdog(struct rq *rq, struct task_struct *p)
        if (!p->signal)
                return;
 
-       soft = p->signal->rlim[RLIMIT_RTTIME].rlim_cur;
-       hard = p->signal->rlim[RLIMIT_RTTIME].rlim_max;
+       /* max may change after cur was read, this will be fixed next tick */
+       soft = task_rlimit(p, RLIMIT_RTTIME);
+       hard = task_rlimit_max(p, RLIMIT_RTTIME);
 
        if (soft != RLIM_INFINITY) {
                unsigned long next;
index 934ae5e687b960ddf78c939db4b0e55936799243..dbd7fe073c558dbc57080e7d0d9344b2cf47b2dd 100644 (file)
@@ -159,6 +159,10 @@ void recalc_sigpending(void)
 
 /* Given the mask, find the first available signal that should be serviced. */
 
+#define SYNCHRONOUS_MASK \
+       (sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \
+        sigmask(SIGTRAP) | sigmask(SIGFPE))
+
 int next_signal(struct sigpending *pending, sigset_t *mask)
 {
        unsigned long i, *s, *m, x;
@@ -166,26 +170,39 @@ int next_signal(struct sigpending *pending, sigset_t *mask)
 
        s = pending->signal.sig;
        m = mask->sig;
+
+       /*
+        * Handle the first word specially: it contains the
+        * synchronous signals that need to be dequeued first.
+        */
+       x = *s &~ *m;
+       if (x) {
+               if (x & SYNCHRONOUS_MASK)
+                       x &= SYNCHRONOUS_MASK;
+               sig = ffz(~x) + 1;
+               return sig;
+       }
+
        switch (_NSIG_WORDS) {
        default:
-               for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)
-                       if ((x = *s &~ *m) != 0) {
-                               sig = ffz(~x) + i*_NSIG_BPW + 1;
-                               break;
-                       }
+               for (i = 1; i < _NSIG_WORDS; ++i) {
+                       x = *++s &~ *++m;
+                       if (!x)
+                               continue;
+                       sig = ffz(~x) + i*_NSIG_BPW + 1;
+                       break;
+               }
                break;
 
-       case 2: if ((x = s[0] &~ m[0]) != 0)
-                       sig = 1;
-               else if ((x = s[1] &~ m[1]) != 0)
-                       sig = _NSIG_BPW + 1;
-               else
+       case 2:
+               x = s[1] &~ m[1];
+               if (!x)
                        break;
-               sig += ffz(~x);
+               sig = ffz(~x) + _NSIG_BPW + 1;
                break;
 
-       case 1: if ((x = *s &~ *m) != 0)
-                       sig = ffz(~x) + 1;
+       case 1:
+               /* Nothing to do */
                break;
        }
 
@@ -228,7 +245,7 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi
 
        if (override_rlimit ||
            atomic_read(&user->sigpending) <=
-                       t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) {
+                       task_rlimit(t, RLIMIT_SIGPENDING)) {
                q = kmem_cache_alloc(sigqueue_cachep, flags);
        } else {
                print_dropped_signal(sig);
index 877fe4f8e05e0439e58edae684bf6a57c6e9e3a9..9814e43fb23b7539354ffc1cf685256ccdb4decf 100644 (file)
@@ -571,8 +571,7 @@ static int set_user(struct cred *new)
        if (!new_user)
                return -EAGAIN;
 
-       if (atomic_read(&new_user->processes) >=
-                               current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
+       if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) &&
                        new_user != INIT_USER) {
                free_uid(new_user);
                return -EAGAIN;
index 33e7a38b6eb9c96d76e5e064b38cda29a69654f2..0ef19c614f6d10cc80c08483b3b6fbaad49b56a7 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/ftrace.h>
 #include <linux/slow-work.h>
 #include <linux/perf_event.h>
+#include <linux/kprobes.h>
 
 #include <asm/uaccess.h>
 #include <asm/processor.h>
@@ -1449,6 +1450,17 @@ static struct ctl_table debug_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+#endif
+#if defined(CONFIG_OPTPROBES)
+       {
+               .procname       = "kprobes-optimization",
+               .data           = &sysctl_kprobes_optimization,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_kprobes_optimization_handler,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
 #endif
        { }
 };
index 8f5d16e0707a403bac2f30887e3b43538328c825..8cd50d8f9bde4c2d2ce403736aa33c9b769f35c9 100644 (file)
@@ -1331,7 +1331,7 @@ static ssize_t binary_sysctl(const int *name, int nlen,
        ssize_t result;
        char *pathname;
        int flags;
-       int acc_mode, fmode;
+       int acc_mode;
 
        pathname = sysctl_getname(name, nlen, &table);
        result = PTR_ERR(pathname);
@@ -1342,15 +1342,12 @@ static ssize_t binary_sysctl(const int *name, int nlen,
        if (oldval && oldlen && newval && newlen) {
                flags = O_RDWR;
                acc_mode = MAY_READ | MAY_WRITE;
-               fmode = FMODE_READ | FMODE_WRITE;
        } else if (newval && newlen) {
                flags = O_WRONLY;
                acc_mode = MAY_WRITE;
-               fmode = FMODE_WRITE;
        } else if (oldval && oldlen) {
                flags = O_RDONLY;
                acc_mode = MAY_READ;
-               fmode = FMODE_READ;
        } else {
                result = 0;
                goto out_putname;
@@ -1361,7 +1358,7 @@ static ssize_t binary_sysctl(const int *name, int nlen,
        if (result)
                goto out_putname;
 
-       result = may_open(&nd.path, acc_mode, fmode);
+       result = may_open(&nd.path, acc_mode, flags);
        if (result)
                goto out_putpath;
 
index 00d59d048edfafdc7ec70462e0133c0ec620a6d8..0a67e041edf82ee3f614959c2643713063e5739b 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/tsacct_kern.h>
 #include <linux/acct.h>
 #include <linux/jiffies.h>
+#include <linux/mm.h>
 
 /*
  * fill in basic accounting fields
index 97b136ff117e364b3d1153c1ae5a0780064ab3de..170d8ca901d8c61faf09a1aff5846c65b96eced1 100644 (file)
@@ -160,6 +160,9 @@ config TEXTSEARCH_BM
 config TEXTSEARCH_FSM
        tristate
 
+config BTREE
+       boolean
+
 config HAS_IOMEM
        boolean
        depends on !NO_IOMEM
index 5e3407d997b2717dee3ef7dee03a503dba980513..b520ec1f33c515d478e3e598a4101b4a8d49f334 100644 (file)
@@ -864,8 +864,7 @@ config DEBUG_FORCE_WEAK_PER_CPU
 
 config LKDTM
        tristate "Linux Kernel Dump Test Tool Module"
-       depends on DEBUG_KERNEL
-       depends on KPROBES
+       depends on DEBUG_FS
        depends on BLOCK
        default n
        help
@@ -876,7 +875,7 @@ config LKDTM
        called lkdtm.
 
        Documentation on how to use the module can be found in
-       drivers/misc/lkdtm.c
+       Documentation/fault-injection/provoke-crashes.txt
 
 config FAULT_INJECTION
        bool "Fault-injection framework"
index 3b0b4a696db95cddd1a675662599d328e9c7e031..2e152aed71980852a4a682a6e98082a01d7048be 100644 (file)
@@ -41,6 +41,7 @@ lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
 obj-$(CONFIG_GENERIC_FIND_LAST_BIT) += find_last_bit.o
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o
+obj-$(CONFIG_BTREE) += btree.o
 obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
 obj-$(CONFIG_DEBUG_LIST) += list_debug.o
 obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
index 11bf49750583af6087e5a65674a4c74247071ea5..ffb78c916ccdf85ffb0d8ec7d0f3f1e4f41ab3ef 100644 (file)
@@ -487,7 +487,7 @@ int __bitmap_parse(const char *buf, unsigned int buflen,
 EXPORT_SYMBOL(__bitmap_parse);
 
 /**
- * bitmap_parse_user()
+ * bitmap_parse_user - convert an ASCII hex string in a user buffer into a bitmap
  *
  * @ubuf: pointer to user buffer containing string.
  * @ulen: buffer size in bytes.  If string is smaller than this
@@ -619,7 +619,7 @@ int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits)
 EXPORT_SYMBOL(bitmap_parselist);
 
 /**
- * bitmap_pos_to_ord(buf, pos, bits)
+ * bitmap_pos_to_ord - find ordinal of set bit at given position in bitmap
  *     @buf: pointer to a bitmap
  *     @pos: a bit position in @buf (0 <= @pos < @bits)
  *     @bits: number of valid bit positions in @buf
@@ -655,7 +655,7 @@ static int bitmap_pos_to_ord(const unsigned long *buf, int pos, int bits)
 }
 
 /**
- * bitmap_ord_to_pos(buf, ord, bits)
+ * bitmap_ord_to_pos - find position of n-th set bit in bitmap
  *     @buf: pointer to bitmap
  *     @ord: ordinal bit position (n-th set bit, n >= 0)
  *     @bits: number of valid bit positions in @buf
@@ -733,10 +733,9 @@ void bitmap_remap(unsigned long *dst, const unsigned long *src,
        bitmap_zero(dst, bits);
 
        w = bitmap_weight(new, bits);
-       for (oldbit = find_first_bit(src, bits);
-            oldbit < bits;
-            oldbit = find_next_bit(src, bits, oldbit + 1)) {
+       for_each_set_bit(oldbit, src, bits) {
                int n = bitmap_pos_to_ord(old, oldbit, bits);
+
                if (n < 0 || w == 0)
                        set_bit(oldbit, dst);   /* identity map */
                else
@@ -903,9 +902,7 @@ void bitmap_onto(unsigned long *dst, const unsigned long *orig,
         */
 
        m = 0;
-       for (n = find_first_bit(relmap, bits);
-            n < bits;
-            n = find_next_bit(relmap, bits, n + 1)) {
+       for_each_set_bit(n, relmap, bits) {
                /* m == bitmap_pos_to_ord(relmap, n, bits) */
                if (test_bit(m, orig))
                        set_bit(n, dst);
@@ -934,9 +931,7 @@ void bitmap_fold(unsigned long *dst, const unsigned long *orig,
                return;
        bitmap_zero(dst, bits);
 
-       for (oldbit = find_first_bit(orig, bits);
-            oldbit < bits;
-            oldbit = find_next_bit(orig, bits, oldbit + 1))
+       for_each_set_bit(oldbit, orig, bits)
                set_bit(oldbit % sz, dst);
 }
 EXPORT_SYMBOL(bitmap_fold);
diff --git a/lib/btree.c b/lib/btree.c
new file mode 100644 (file)
index 0000000..41859a8
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * lib/btree.c - Simple In-memory B+Tree
+ *
+ * As should be obvious for Linux kernel code, license is GPLv2
+ *
+ * Copyright (c) 2007-2008 Joern Engel <joern@logfs.org>
+ * Bits and pieces stolen from Peter Zijlstra's code, which is
+ * Copyright 2007, Red Hat Inc. Peter Zijlstra <pzijlstr@redhat.com>
+ * GPLv2
+ *
+ * see http://programming.kicks-ass.net/kernel-patches/vma_lookup/btree.patch
+ *
+ * A relatively simple B+Tree implementation.  I have written it as a learning
+ * excercise to understand how B+Trees work.  Turned out to be useful as well.
+ *
+ * B+Trees can be used similar to Linux radix trees (which don't have anything
+ * in common with textbook radix trees, beware).  Prerequisite for them working
+ * well is that access to a random tree node is much faster than a large number
+ * of operations within each node.
+ *
+ * Disks have fulfilled the prerequisite for a long time.  More recently DRAM
+ * has gained similar properties, as memory access times, when measured in cpu
+ * cycles, have increased.  Cacheline sizes have increased as well, which also
+ * helps B+Trees.
+ *
+ * Compared to radix trees, B+Trees are more efficient when dealing with a
+ * sparsely populated address space.  Between 25% and 50% of the memory is
+ * occupied with valid pointers.  When densely populated, radix trees contain
+ * ~98% pointers - hard to beat.  Very sparse radix trees contain only ~2%
+ * pointers.
+ *
+ * This particular implementation stores pointers identified by a long value.
+ * Storing NULL pointers is illegal, lookup will return NULL when no entry
+ * was found.
+ *
+ * A tricks was used that is not commonly found in textbooks.  The lowest
+ * values are to the right, not to the left.  All used slots within a node
+ * are on the left, all unused slots contain NUL values.  Most operations
+ * simply loop once over all slots and terminate on the first NUL.
+ */
+
+#include <linux/btree.h>
+#include <linux/cache.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define NODESIZE MAX(L1_CACHE_BYTES, 128)
+
+struct btree_geo {
+       int keylen;
+       int no_pairs;
+       int no_longs;
+};
+
+struct btree_geo btree_geo32 = {
+       .keylen = 1,
+       .no_pairs = NODESIZE / sizeof(long) / 2,
+       .no_longs = NODESIZE / sizeof(long) / 2,
+};
+EXPORT_SYMBOL_GPL(btree_geo32);
+
+#define LONG_PER_U64 (64 / BITS_PER_LONG)
+struct btree_geo btree_geo64 = {
+       .keylen = LONG_PER_U64,
+       .no_pairs = NODESIZE / sizeof(long) / (1 + LONG_PER_U64),
+       .no_longs = LONG_PER_U64 * (NODESIZE / sizeof(long) / (1 + LONG_PER_U64)),
+};
+EXPORT_SYMBOL_GPL(btree_geo64);
+
+struct btree_geo btree_geo128 = {
+       .keylen = 2 * LONG_PER_U64,
+       .no_pairs = NODESIZE / sizeof(long) / (1 + 2 * LONG_PER_U64),
+       .no_longs = 2 * LONG_PER_U64 * (NODESIZE / sizeof(long) / (1 + 2 * LONG_PER_U64)),
+};
+EXPORT_SYMBOL_GPL(btree_geo128);
+
+static struct kmem_cache *btree_cachep;
+
+void *btree_alloc(gfp_t gfp_mask, void *pool_data)
+{
+       return kmem_cache_alloc(btree_cachep, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(btree_alloc);
+
+void btree_free(void *element, void *pool_data)
+{
+       kmem_cache_free(btree_cachep, element);
+}
+EXPORT_SYMBOL_GPL(btree_free);
+
+static unsigned long *btree_node_alloc(struct btree_head *head, gfp_t gfp)
+{
+       unsigned long *node;
+
+       node = mempool_alloc(head->mempool, gfp);
+       memset(node, 0, NODESIZE);
+       return node;
+}
+
+static int longcmp(const unsigned long *l1, const unsigned long *l2, size_t n)
+{
+       size_t i;
+
+       for (i = 0; i < n; i++) {
+               if (l1[i] < l2[i])
+                       return -1;
+               if (l1[i] > l2[i])
+                       return 1;
+       }
+       return 0;
+}
+
+static unsigned long *longcpy(unsigned long *dest, const unsigned long *src,
+               size_t n)
+{
+       size_t i;
+
+       for (i = 0; i < n; i++)
+               dest[i] = src[i];
+       return dest;
+}
+
+static unsigned long *longset(unsigned long *s, unsigned long c, size_t n)
+{
+       size_t i;
+
+       for (i = 0; i < n; i++)
+               s[i] = c;
+       return s;
+}
+
+static void dec_key(struct btree_geo *geo, unsigned long *key)
+{
+       unsigned long val;
+       int i;
+
+       for (i = geo->keylen - 1; i >= 0; i--) {
+               val = key[i];
+               key[i] = val - 1;
+               if (val)
+                       break;
+       }
+}
+
+static unsigned long *bkey(struct btree_geo *geo, unsigned long *node, int n)
+{
+       return &node[n * geo->keylen];
+}
+
+static void *bval(struct btree_geo *geo, unsigned long *node, int n)
+{
+       return (void *)node[geo->no_longs + n];
+}
+
+static void setkey(struct btree_geo *geo, unsigned long *node, int n,
+                  unsigned long *key)
+{
+       longcpy(bkey(geo, node, n), key, geo->keylen);
+}
+
+static void setval(struct btree_geo *geo, unsigned long *node, int n,
+                  void *val)
+{
+       node[geo->no_longs + n] = (unsigned long) val;
+}
+
+static void clearpair(struct btree_geo *geo, unsigned long *node, int n)
+{
+       longset(bkey(geo, node, n), 0, geo->keylen);
+       node[geo->no_longs + n] = 0;
+}
+
+static inline void __btree_init(struct btree_head *head)
+{
+       head->node = NULL;
+       head->height = 0;
+}
+
+void btree_init_mempool(struct btree_head *head, mempool_t *mempool)
+{
+       __btree_init(head);
+       head->mempool = mempool;
+}
+EXPORT_SYMBOL_GPL(btree_init_mempool);
+
+int btree_init(struct btree_head *head)
+{
+       __btree_init(head);
+       head->mempool = mempool_create(0, btree_alloc, btree_free, NULL);
+       if (!head->mempool)
+               return -ENOMEM;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btree_init);
+
+void btree_destroy(struct btree_head *head)
+{
+       mempool_destroy(head->mempool);
+       head->mempool = NULL;
+}
+EXPORT_SYMBOL_GPL(btree_destroy);
+
+void *btree_last(struct btree_head *head, struct btree_geo *geo,
+                unsigned long *key)
+{
+       int height = head->height;
+       unsigned long *node = head->node;
+
+       if (height == 0)
+               return NULL;
+
+       for ( ; height > 1; height--)
+               node = bval(geo, node, 0);
+
+       longcpy(key, bkey(geo, node, 0), geo->keylen);
+       return bval(geo, node, 0);
+}
+EXPORT_SYMBOL_GPL(btree_last);
+
+static int keycmp(struct btree_geo *geo, unsigned long *node, int pos,
+                 unsigned long *key)
+{
+       return longcmp(bkey(geo, node, pos), key, geo->keylen);
+}
+
+static int keyzero(struct btree_geo *geo, unsigned long *key)
+{
+       int i;
+
+       for (i = 0; i < geo->keylen; i++)
+               if (key[i])
+                       return 0;
+
+       return 1;
+}
+
+void *btree_lookup(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key)
+{
+       int i, height = head->height;
+       unsigned long *node = head->node;
+
+       if (height == 0)
+               return NULL;
+
+       for ( ; height > 1; height--) {
+               for (i = 0; i < geo->no_pairs; i++)
+                       if (keycmp(geo, node, i, key) <= 0)
+                               break;
+               if (i == geo->no_pairs)
+                       return NULL;
+               node = bval(geo, node, i);
+               if (!node)
+                       return NULL;
+       }
+
+       if (!node)
+               return NULL;
+
+       for (i = 0; i < geo->no_pairs; i++)
+               if (keycmp(geo, node, i, key) == 0)
+                       return bval(geo, node, i);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(btree_lookup);
+
+int btree_update(struct btree_head *head, struct btree_geo *geo,
+                unsigned long *key, void *val)
+{
+       int i, height = head->height;
+       unsigned long *node = head->node;
+
+       if (height == 0)
+               return -ENOENT;
+
+       for ( ; height > 1; height--) {
+               for (i = 0; i < geo->no_pairs; i++)
+                       if (keycmp(geo, node, i, key) <= 0)
+                               break;
+               if (i == geo->no_pairs)
+                       return -ENOENT;
+               node = bval(geo, node, i);
+               if (!node)
+                       return -ENOENT;
+       }
+
+       if (!node)
+               return -ENOENT;
+
+       for (i = 0; i < geo->no_pairs; i++)
+               if (keycmp(geo, node, i, key) == 0) {
+                       setval(geo, node, i, val);
+                       return 0;
+               }
+       return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(btree_update);
+
+/*
+ * Usually this function is quite similar to normal lookup.  But the key of
+ * a parent node may be smaller than the smallest key of all its siblings.
+ * In such a case we cannot just return NULL, as we have only proven that no
+ * key smaller than __key, but larger than this parent key exists.
+ * So we set __key to the parent key and retry.  We have to use the smallest
+ * such parent key, which is the last parent key we encountered.
+ */
+void *btree_get_prev(struct btree_head *head, struct btree_geo *geo,
+                    unsigned long *__key)
+{
+       int i, height;
+       unsigned long *node, *oldnode;
+       unsigned long *retry_key = NULL, key[geo->keylen];
+
+       if (keyzero(geo, __key))
+               return NULL;
+
+       if (head->height == 0)
+               return NULL;
+retry:
+       longcpy(key, __key, geo->keylen);
+       dec_key(geo, key);
+
+       node = head->node;
+       for (height = head->height ; height > 1; height--) {
+               for (i = 0; i < geo->no_pairs; i++)
+                       if (keycmp(geo, node, i, key) <= 0)
+                               break;
+               if (i == geo->no_pairs)
+                       goto miss;
+               oldnode = node;
+               node = bval(geo, node, i);
+               if (!node)
+                       goto miss;
+               retry_key = bkey(geo, oldnode, i);
+       }
+
+       if (!node)
+               goto miss;
+
+       for (i = 0; i < geo->no_pairs; i++) {
+               if (keycmp(geo, node, i, key) <= 0) {
+                       if (bval(geo, node, i)) {
+                               longcpy(__key, bkey(geo, node, i), geo->keylen);
+                               return bval(geo, node, i);
+                       } else
+                               goto miss;
+               }
+       }
+miss:
+       if (retry_key) {
+               __key = retry_key;
+               retry_key = NULL;
+               goto retry;
+       }
+       return NULL;
+}
+
+static int getpos(struct btree_geo *geo, unsigned long *node,
+               unsigned long *key)
+{
+       int i;
+
+       for (i = 0; i < geo->no_pairs; i++) {
+               if (keycmp(geo, node, i, key) <= 0)
+                       break;
+       }
+       return i;
+}
+
+static int getfill(struct btree_geo *geo, unsigned long *node, int start)
+{
+       int i;
+
+       for (i = start; i < geo->no_pairs; i++)
+               if (!bval(geo, node, i))
+                       break;
+       return i;
+}
+
+/*
+ * locate the correct leaf node in the btree
+ */
+static unsigned long *find_level(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key, int level)
+{
+       unsigned long *node = head->node;
+       int i, height;
+
+       for (height = head->height; height > level; height--) {
+               for (i = 0; i < geo->no_pairs; i++)
+                       if (keycmp(geo, node, i, key) <= 0)
+                               break;
+
+               if ((i == geo->no_pairs) || !bval(geo, node, i)) {
+                       /* right-most key is too large, update it */
+                       /* FIXME: If the right-most key on higher levels is
+                        * always zero, this wouldn't be necessary. */
+                       i--;
+                       setkey(geo, node, i, key);
+               }
+               BUG_ON(i < 0);
+               node = bval(geo, node, i);
+       }
+       BUG_ON(!node);
+       return node;
+}
+
+static int btree_grow(struct btree_head *head, struct btree_geo *geo,
+                     gfp_t gfp)
+{
+       unsigned long *node;
+       int fill;
+
+       node = btree_node_alloc(head, gfp);
+       if (!node)
+               return -ENOMEM;
+       if (head->node) {
+               fill = getfill(geo, head->node, 0);
+               setkey(geo, node, 0, bkey(geo, head->node, fill - 1));
+               setval(geo, node, 0, head->node);
+       }
+       head->node = node;
+       head->height++;
+       return 0;
+}
+
+static void btree_shrink(struct btree_head *head, struct btree_geo *geo)
+{
+       unsigned long *node;
+       int fill;
+
+       if (head->height <= 1)
+               return;
+
+       node = head->node;
+       fill = getfill(geo, node, 0);
+       BUG_ON(fill > 1);
+       head->node = bval(geo, node, 0);
+       head->height--;
+       mempool_free(node, head->mempool);
+}
+
+static int btree_insert_level(struct btree_head *head, struct btree_geo *geo,
+                             unsigned long *key, void *val, int level,
+                             gfp_t gfp)
+{
+       unsigned long *node;
+       int i, pos, fill, err;
+
+       BUG_ON(!val);
+       if (head->height < level) {
+               err = btree_grow(head, geo, gfp);
+               if (err)
+                       return err;
+       }
+
+retry:
+       node = find_level(head, geo, key, level);
+       pos = getpos(geo, node, key);
+       fill = getfill(geo, node, pos);
+       /* two identical keys are not allowed */
+       BUG_ON(pos < fill && keycmp(geo, node, pos, key) == 0);
+
+       if (fill == geo->no_pairs) {
+               /* need to split node */
+               unsigned long *new;
+
+               new = btree_node_alloc(head, gfp);
+               if (!new)
+                       return -ENOMEM;
+               err = btree_insert_level(head, geo,
+                               bkey(geo, node, fill / 2 - 1),
+                               new, level + 1, gfp);
+               if (err) {
+                       mempool_free(new, head->mempool);
+                       return err;
+               }
+               for (i = 0; i < fill / 2; i++) {
+                       setkey(geo, new, i, bkey(geo, node, i));
+                       setval(geo, new, i, bval(geo, node, i));
+                       setkey(geo, node, i, bkey(geo, node, i + fill / 2));
+                       setval(geo, node, i, bval(geo, node, i + fill / 2));
+                       clearpair(geo, node, i + fill / 2);
+               }
+               if (fill & 1) {
+                       setkey(geo, node, i, bkey(geo, node, fill - 1));
+                       setval(geo, node, i, bval(geo, node, fill - 1));
+                       clearpair(geo, node, fill - 1);
+               }
+               goto retry;
+       }
+       BUG_ON(fill >= geo->no_pairs);
+
+       /* shift and insert */
+       for (i = fill; i > pos; i--) {
+               setkey(geo, node, i, bkey(geo, node, i - 1));
+               setval(geo, node, i, bval(geo, node, i - 1));
+       }
+       setkey(geo, node, pos, key);
+       setval(geo, node, pos, val);
+
+       return 0;
+}
+
+int btree_insert(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key, void *val, gfp_t gfp)
+{
+       return btree_insert_level(head, geo, key, val, 1, gfp);
+}
+EXPORT_SYMBOL_GPL(btree_insert);
+
+static void *btree_remove_level(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key, int level);
+static void merge(struct btree_head *head, struct btree_geo *geo, int level,
+               unsigned long *left, int lfill,
+               unsigned long *right, int rfill,
+               unsigned long *parent, int lpos)
+{
+       int i;
+
+       for (i = 0; i < rfill; i++) {
+               /* Move all keys to the left */
+               setkey(geo, left, lfill + i, bkey(geo, right, i));
+               setval(geo, left, lfill + i, bval(geo, right, i));
+       }
+       /* Exchange left and right child in parent */
+       setval(geo, parent, lpos, right);
+       setval(geo, parent, lpos + 1, left);
+       /* Remove left (formerly right) child from parent */
+       btree_remove_level(head, geo, bkey(geo, parent, lpos), level + 1);
+       mempool_free(right, head->mempool);
+}
+
+static void rebalance(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key, int level, unsigned long *child, int fill)
+{
+       unsigned long *parent, *left = NULL, *right = NULL;
+       int i, no_left, no_right;
+
+       if (fill == 0) {
+               /* Because we don't steal entries from a neigbour, this case
+                * can happen.  Parent node contains a single child, this
+                * node, so merging with a sibling never happens.
+                */
+               btree_remove_level(head, geo, key, level + 1);
+               mempool_free(child, head->mempool);
+               return;
+       }
+
+       parent = find_level(head, geo, key, level + 1);
+       i = getpos(geo, parent, key);
+       BUG_ON(bval(geo, parent, i) != child);
+
+       if (i > 0) {
+               left = bval(geo, parent, i - 1);
+               no_left = getfill(geo, left, 0);
+               if (fill + no_left <= geo->no_pairs) {
+                       merge(head, geo, level,
+                                       left, no_left,
+                                       child, fill,
+                                       parent, i - 1);
+                       return;
+               }
+       }
+       if (i + 1 < getfill(geo, parent, i)) {
+               right = bval(geo, parent, i + 1);
+               no_right = getfill(geo, right, 0);
+               if (fill + no_right <= geo->no_pairs) {
+                       merge(head, geo, level,
+                                       child, fill,
+                                       right, no_right,
+                                       parent, i);
+                       return;
+               }
+       }
+       /*
+        * We could also try to steal one entry from the left or right
+        * neighbor.  By not doing so we changed the invariant from
+        * "all nodes are at least half full" to "no two neighboring
+        * nodes can be merged".  Which means that the average fill of
+        * all nodes is still half or better.
+        */
+}
+
+static void *btree_remove_level(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key, int level)
+{
+       unsigned long *node;
+       int i, pos, fill;
+       void *ret;
+
+       if (level > head->height) {
+               /* we recursed all the way up */
+               head->height = 0;
+               head->node = NULL;
+               return NULL;
+       }
+
+       node = find_level(head, geo, key, level);
+       pos = getpos(geo, node, key);
+       fill = getfill(geo, node, pos);
+       if ((level == 1) && (keycmp(geo, node, pos, key) != 0))
+               return NULL;
+       ret = bval(geo, node, pos);
+
+       /* remove and shift */
+       for (i = pos; i < fill - 1; i++) {
+               setkey(geo, node, i, bkey(geo, node, i + 1));
+               setval(geo, node, i, bval(geo, node, i + 1));
+       }
+       clearpair(geo, node, fill - 1);
+
+       if (fill - 1 < geo->no_pairs / 2) {
+               if (level < head->height)
+                       rebalance(head, geo, key, level, node, fill - 1);
+               else if (fill - 1 == 1)
+                       btree_shrink(head, geo);
+       }
+
+       return ret;
+}
+
+void *btree_remove(struct btree_head *head, struct btree_geo *geo,
+               unsigned long *key)
+{
+       if (head->height == 0)
+               return NULL;
+
+       return btree_remove_level(head, geo, key, 1);
+}
+EXPORT_SYMBOL_GPL(btree_remove);
+
+int btree_merge(struct btree_head *target, struct btree_head *victim,
+               struct btree_geo *geo, gfp_t gfp)
+{
+       unsigned long key[geo->keylen];
+       unsigned long dup[geo->keylen];
+       void *val;
+       int err;
+
+       BUG_ON(target == victim);
+
+       if (!(target->node)) {
+               /* target is empty, just copy fields over */
+               target->node = victim->node;
+               target->height = victim->height;
+               __btree_init(victim);
+               return 0;
+       }
+
+       /* TODO: This needs some optimizations.  Currently we do three tree
+        * walks to remove a single object from the victim.
+        */
+       for (;;) {
+               if (!btree_last(victim, geo, key))
+                       break;
+               val = btree_lookup(victim, geo, key);
+               err = btree_insert(target, geo, key, val, gfp);
+               if (err)
+                       return err;
+               /* We must make a copy of the key, as the original will get
+                * mangled inside btree_remove. */
+               longcpy(dup, key, geo->keylen);
+               btree_remove(victim, geo, dup);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btree_merge);
+
+static size_t __btree_for_each(struct btree_head *head, struct btree_geo *geo,
+                              unsigned long *node, unsigned long opaque,
+                              void (*func)(void *elem, unsigned long opaque,
+                                           unsigned long *key, size_t index,
+                                           void *func2),
+                              void *func2, int reap, int height, size_t count)
+{
+       int i;
+       unsigned long *child;
+
+       for (i = 0; i < geo->no_pairs; i++) {
+               child = bval(geo, node, i);
+               if (!child)
+                       break;
+               if (height > 1)
+                       count = __btree_for_each(head, geo, child, opaque,
+                                       func, func2, reap, height - 1, count);
+               else
+                       func(child, opaque, bkey(geo, node, i), count++,
+                                       func2);
+       }
+       if (reap)
+               mempool_free(node, head->mempool);
+       return count;
+}
+
+static void empty(void *elem, unsigned long opaque, unsigned long *key,
+                 size_t index, void *func2)
+{
+}
+
+void visitorl(void *elem, unsigned long opaque, unsigned long *key,
+             size_t index, void *__func)
+{
+       visitorl_t func = __func;
+
+       func(elem, opaque, *key, index);
+}
+EXPORT_SYMBOL_GPL(visitorl);
+
+void visitor32(void *elem, unsigned long opaque, unsigned long *__key,
+              size_t index, void *__func)
+{
+       visitor32_t func = __func;
+       u32 *key = (void *)__key;
+
+       func(elem, opaque, *key, index);
+}
+EXPORT_SYMBOL_GPL(visitor32);
+
+void visitor64(void *elem, unsigned long opaque, unsigned long *__key,
+              size_t index, void *__func)
+{
+       visitor64_t func = __func;
+       u64 *key = (void *)__key;
+
+       func(elem, opaque, *key, index);
+}
+EXPORT_SYMBOL_GPL(visitor64);
+
+void visitor128(void *elem, unsigned long opaque, unsigned long *__key,
+               size_t index, void *__func)
+{
+       visitor128_t func = __func;
+       u64 *key = (void *)__key;
+
+       func(elem, opaque, key[0], key[1], index);
+}
+EXPORT_SYMBOL_GPL(visitor128);
+
+size_t btree_visitor(struct btree_head *head, struct btree_geo *geo,
+                    unsigned long opaque,
+                    void (*func)(void *elem, unsigned long opaque,
+                                 unsigned long *key,
+                                 size_t index, void *func2),
+                    void *func2)
+{
+       size_t count = 0;
+
+       if (!func2)
+               func = empty;
+       if (head->node)
+               count = __btree_for_each(head, geo, head->node, opaque, func,
+                               func2, 0, head->height, 0);
+       return count;
+}
+EXPORT_SYMBOL_GPL(btree_visitor);
+
+size_t btree_grim_visitor(struct btree_head *head, struct btree_geo *geo,
+                         unsigned long opaque,
+                         void (*func)(void *elem, unsigned long opaque,
+                                      unsigned long *key,
+                                      size_t index, void *func2),
+                         void *func2)
+{
+       size_t count = 0;
+
+       if (!func2)
+               func = empty;
+       if (head->node)
+               count = __btree_for_each(head, geo, head->node, opaque, func,
+                               func2, 1, head->height, 0);
+       __btree_init(head);
+       return count;
+}
+EXPORT_SYMBOL_GPL(btree_grim_visitor);
+
+static int __init btree_module_init(void)
+{
+       btree_cachep = kmem_cache_create("btree_node", NODESIZE, 0,
+                       SLAB_HWCACHE_ALIGN, NULL);
+       return 0;
+}
+
+static void __exit btree_module_exit(void)
+{
+       kmem_cache_destroy(btree_cachep);
+}
+
+/* If core code starts using btree, initialization should happen even earlier */
+module_init(btree_module_init);
+module_exit(btree_module_exit);
+
+MODULE_AUTHOR("Joern Engel <joern@logfs.org>");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
index 02e3b31b3a79003514138cf5c6a1cad19e3d3cf9..0f45fbff34cb970b68dd4642eb30b67d3857a31e 100644 (file)
 #include <asm/atomic.h>
 #include "crc32defs.h"
 #if CRC_LE_BITS == 8
-#define tole(x) __constant_cpu_to_le32(x)
-#define tobe(x) __constant_cpu_to_be32(x)
+# define tole(x) __constant_cpu_to_le32(x)
 #else
-#define tole(x) (x)
-#define tobe(x) (x)
+# define tole(x) (x)
+#endif
+
+#if CRC_BE_BITS == 8
+# define tobe(x) __constant_cpu_to_be32(x)
+#else
+# define tobe(x) (x)
 #endif
 #include "crc32table.h"
 
@@ -52,20 +56,19 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 *tab)
 # else
 #  define DO_CRC(x) crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc << 8)
 # endif
-       const u32 *b = (const u32 *)buf;
+       const u32 *b;
        size_t    rem_len;
 
        /* Align it */
-       if (unlikely((long)b & 3 && len)) {
-               u8 *p = (u8 *)b;
+       if (unlikely((long)buf & 3 && len)) {
                do {
-                       DO_CRC(*p++);
-               } while ((--len) && ((long)p)&3);
-               b = (u32 *)p;
+                       DO_CRC(*buf++);
+               } while ((--len) && ((long)buf)&3);
        }
        rem_len = len & 3;
        /* load data 32 bits wide, xor data 32 bits wide. */
        len = len >> 2;
+       b = (const u32 *)buf;
        for (--b; len; --len) {
                crc ^= *++b; /* use pre increment for speed */
                DO_CRC(0);
@@ -82,6 +85,7 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 *tab)
                } while (--len);
        }
        return crc;
+#undef DO_CRC
 }
 #endif
 /**
@@ -119,9 +123,6 @@ u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len)
        crc = __cpu_to_le32(crc);
        crc = crc32_body(crc, p, len, tab);
        return __le32_to_cpu(crc);
-#undef ENDIAN_SHIFT
-#undef DO_CRC
-
 # elif CRC_LE_BITS == 4
        while (len--) {
                crc ^= *p++;
@@ -179,9 +180,6 @@ u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len)
        crc = __cpu_to_be32(crc);
        crc = crc32_body(crc, p, len, tab);
        return __be32_to_cpu(crc);
-#undef ENDIAN_SHIFT
-#undef DO_CRC
-
 # elif CRC_BE_BITS == 4
        while (len--) {
                crc ^= *p++ << 24;
index 19d11e0bb958a874fe7f560f25c75e392cd47aea..4b5cb794c38bb270b8b72b70a47de265ec05c210 100644 (file)
 #include <linux/slab.h>
 #include <linux/list.h>
 
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+static struct list_head *merge(void *priv,
+                               int (*cmp)(void *priv, struct list_head *a,
+                                       struct list_head *b),
+                               struct list_head *a, struct list_head *b)
+{
+       struct list_head head, *tail = &head;
+
+       while (a && b) {
+               /* if equal, take 'a' -- important for sort stability */
+               if ((*cmp)(priv, a, b) <= 0) {
+                       tail->next = a;
+                       a = a->next;
+               } else {
+                       tail->next = b;
+                       b = b->next;
+               }
+               tail = tail->next;
+       }
+       tail->next = a?:b;
+       return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure.  This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+static void merge_and_restore_back_links(void *priv,
+                               int (*cmp)(void *priv, struct list_head *a,
+                                       struct list_head *b),
+                               struct list_head *head,
+                               struct list_head *a, struct list_head *b)
+{
+       struct list_head *tail = head;
+
+       while (a && b) {
+               /* if equal, take 'a' -- important for sort stability */
+               if ((*cmp)(priv, a, b) <= 0) {
+                       tail->next = a;
+                       a->prev = tail;
+                       a = a->next;
+               } else {
+                       tail->next = b;
+                       b->prev = tail;
+                       b = b->next;
+               }
+               tail = tail->next;
+       }
+       tail->next = a ? : b;
+
+       do {
+               /*
+                * In worst cases this loop may run many iterations.
+                * Continue callbacks to the client even though no
+                * element comparison is needed, so the client's cmp()
+                * routine can invoke cond_resched() periodically.
+                */
+               (*cmp)(priv, tail, tail);
+
+               tail->next->prev = tail;
+               tail = tail->next;
+       } while (tail->next);
+
+       tail->next = head;
+       head->prev = tail;
+}
+
 /**
- * list_sort - sort a list.
- * @priv: private data, passed to @cmp
+ * list_sort - sort a list
+ * @priv: private data, opaque to list_sort(), passed to @cmp
  * @head: the list to sort
  * @cmp: the elements comparison function
  *
- * This function has been implemented by Mark J Roberts <mjr@znex.org>. It
- * implements "merge sort" which has O(nlog(n)) complexity. The list is sorted
- * in ascending order.
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
  *
- * The comparison function @cmp is supposed to return a negative value if @a is
- * less than @b, and a positive value if @a is greater than @b. If @a and @b
- * are equivalent, then it does not matter what this function returns.
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
  */
 void list_sort(void *priv, struct list_head *head,
-              int (*cmp)(void *priv, struct list_head *a,
-                         struct list_head *b))
+               int (*cmp)(void *priv, struct list_head *a,
+                       struct list_head *b))
 {
-       struct list_head *p, *q, *e, *list, *tail, *oldhead;
-       int insize, nmerges, psize, qsize, i;
+       struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+                                               -- last slot is a sentinel */
+       int lev;  /* index into part[] */
+       int max_lev = 0;
+       struct list_head *list;
 
        if (list_empty(head))
                return;
 
+       memset(part, 0, sizeof(part));
+
+       head->prev->next = NULL;
        list = head->next;
-       list_del(head);
-       insize = 1;
-       for (;;) {
-               p = oldhead = list;
-               list = tail = NULL;
-               nmerges = 0;
-
-               while (p) {
-                       nmerges++;
-                       q = p;
-                       psize = 0;
-                       for (i = 0; i < insize; i++) {
-                               psize++;
-                               q = q->next == oldhead ? NULL : q->next;
-                               if (!q)
-                                       break;
-                       }
 
-                       qsize = insize;
-                       while (psize > 0 || (qsize > 0 && q)) {
-                               if (!psize) {
-                                       e = q;
-                                       q = q->next;
-                                       qsize--;
-                                       if (q == oldhead)
-                                               q = NULL;
-                               } else if (!qsize || !q) {
-                                       e = p;
-                                       p = p->next;
-                                       psize--;
-                                       if (p == oldhead)
-                                               p = NULL;
-                               } else if (cmp(priv, p, q) <= 0) {
-                                       e = p;
-                                       p = p->next;
-                                       psize--;
-                                       if (p == oldhead)
-                                               p = NULL;
-                               } else {
-                                       e = q;
-                                       q = q->next;
-                                       qsize--;
-                                       if (q == oldhead)
-                                               q = NULL;
-                               }
-                               if (tail)
-                                       tail->next = e;
-                               else
-                                       list = e;
-                               e->prev = tail;
-                               tail = e;
+       while (list) {
+               struct list_head *cur = list;
+               list = list->next;
+               cur->next = NULL;
+
+               for (lev = 0; part[lev]; lev++) {
+                       cur = merge(priv, cmp, part[lev], cur);
+                       part[lev] = NULL;
+               }
+               if (lev > max_lev) {
+                       if (unlikely(lev >= ARRAY_SIZE(part)-1)) {
+                               printk_once(KERN_DEBUG "list passed to"
+                                       " list_sort() too long for"
+                                       " efficiency\n");
+                               lev--;
                        }
-                       p = q;
+                       max_lev = lev;
                }
+               part[lev] = cur;
+       }
 
-               tail->next = list;
-               list->prev = tail;
+       for (lev = 0; lev < max_lev; lev++)
+               if (part[lev])
+                       list = merge(priv, cmp, part[lev], list);
 
-               if (nmerges <= 1)
-                       break;
+       merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
+}
+EXPORT_SYMBOL(list_sort);
 
-               insize *= 2;
-       }
+#ifdef DEBUG_LIST_SORT
+struct debug_el {
+       struct list_head l_h;
+       int value;
+       unsigned serial;
+};
 
-       head->next = list;
-       head->prev = list->prev;
-       list->prev->next = head;
-       list->prev = head;
+static int cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+       return container_of(a, struct debug_el, l_h)->value
+            - container_of(b, struct debug_el, l_h)->value;
 }
 
-EXPORT_SYMBOL(list_sort);
+/*
+ * The pattern of set bits in the list length determines which cases
+ * are hit in list_sort().
+ */
+#define LIST_SORT_TEST_LENGTH (512+128+2) /* not including head */
+
+static int __init list_sort_test(void)
+{
+       int i, r = 1, count;
+       struct list_head *head = kmalloc(sizeof(*head), GFP_KERNEL);
+       struct list_head *cur;
+
+       printk(KERN_WARNING "testing list_sort()\n");
+
+       cur = head;
+       for (i = 0; i < LIST_SORT_TEST_LENGTH; i++) {
+               struct debug_el *el = kmalloc(sizeof(*el), GFP_KERNEL);
+               BUG_ON(!el);
+                /* force some equivalencies */
+               el->value = (r = (r * 725861) % 6599) % (LIST_SORT_TEST_LENGTH/3);
+               el->serial = i;
+
+               el->l_h.prev = cur;
+               cur->next = &el->l_h;
+               cur = cur->next;
+       }
+       head->prev = cur;
+
+       list_sort(NULL, head, cmp);
+
+       count = 1;
+       for (cur = head->next; cur->next != head; cur = cur->next) {
+               struct debug_el *el = container_of(cur, struct debug_el, l_h);
+               int cmp_result = cmp(NULL, cur, cur->next);
+               if (cur->next->prev != cur) {
+                       printk(KERN_EMERG "list_sort() returned "
+                                               "a corrupted list!\n");
+                       return 1;
+               } else if (cmp_result > 0) {
+                       printk(KERN_EMERG "list_sort() failed to sort!\n");
+                       return 1;
+               } else if (cmp_result == 0 &&
+                               el->serial >= container_of(cur->next,
+                                       struct debug_el, l_h)->serial) {
+                       printk(KERN_EMERG "list_sort() failed to preserve order"
+                                                " of equivalent elements!\n");
+                       return 1;
+               }
+               kfree(cur->prev);
+               count++;
+       }
+       kfree(cur);
+       if (count != LIST_SORT_TEST_LENGTH) {
+               printk(KERN_EMERG "list_sort() returned list of"
+                                               "different length!\n");
+               return 1;
+       }
+       return 0;
+}
+module_init(list_sort_test);
+#endif
index 238e72a18ce1ea7ee0d1849a18d40ae56af5c121..fdc77c82f922a2078778c422454d9da83253f08f 100644 (file)
@@ -15,7 +15,7 @@ void show_mem(void)
        unsigned long total = 0, reserved = 0, shared = 0,
                nonshared = 0, highmem = 0;
 
-       printk(KERN_INFO "Mem-Info:\n");
+       printk("Mem-Info:\n");
        show_free_areas();
 
        for_each_online_pgdat(pgdat) {
@@ -49,15 +49,15 @@ void show_mem(void)
                pgdat_resize_unlock(pgdat, &flags);
        }
 
-       printk(KERN_INFO "%lu pages RAM\n", total);
+       printk("%lu pages RAM\n", total);
 #ifdef CONFIG_HIGHMEM
-       printk(KERN_INFO "%lu pages HighMem\n", highmem);
+       printk("%lu pages HighMem\n", highmem);
 #endif
-       printk(KERN_INFO "%lu pages reserved\n", reserved);
-       printk(KERN_INFO "%lu pages shared\n", shared);
-       printk(KERN_INFO "%lu pages non-shared\n", nonshared);
+       printk("%lu pages reserved\n", reserved);
+       printk("%lu pages shared\n", shared);
+       printk("%lu pages non-shared\n", nonshared);
 #ifdef CONFIG_QUICKLIST
-       printk(KERN_INFO "%lu pages in pagetable cache\n",
+       printk("%lu pages in pagetable cache\n",
                quicklist_total_size());
 #endif
 }
index a1cdcfcc42d06db93e81928f022d4c70b82959be..f71bead1be3efaf5694cbbe1df1d59edb18b74ae 100644 (file)
@@ -36,25 +36,21 @@ int strnicmp(const char *s1, const char *s2, size_t len)
        /* Yes, Virginia, it had better be unsigned */
        unsigned char c1, c2;
 
-       c1 = c2 = 0;
-       if (len) {
-               do {
-                       c1 = *s1;
-                       c2 = *s2;
-                       s1++;
-                       s2++;
-                       if (!c1)
-                               break;
-                       if (!c2)
-                               break;
-                       if (c1 == c2)
-                               continue;
-                       c1 = tolower(c1);
-                       c2 = tolower(c2);
-                       if (c1 != c2)
-                               break;
-               } while (--len);
-       }
+       if (!len)
+               return 0;
+
+       do {
+               c1 = *s1++;
+               c2 = *s2++;
+               if (!c1 || !c2)
+                       break;
+               if (c1 == c2)
+                       continue;
+               c1 = tolower(c1);
+               c2 = tolower(c2);
+               if (c1 != c2)
+                       break;
+       } while (--len);
        return (int)c1 - (int)c2;
 }
 EXPORT_SYMBOL(strnicmp);
@@ -693,13 +689,13 @@ EXPORT_SYMBOL(strstr);
  */
 char *strnstr(const char *s1, const char *s2, size_t len)
 {
-       size_t l1 = len, l2;
+       size_t l2;
 
        l2 = strlen(s2);
        if (!l2)
                return (char *)s1;
-       while (l1 >= l2) {
-               l1--;
+       while (len >= l2) {
+               len--;
                if (!memcmp(s1, s2, l2))
                        return (char *)s1;
                s1++;
index af4aaa6c36f36c924d9a63da70f84e2b3459fca1..0d461c7c14db75eaecceb2104d6bc1e901c5971c 100644 (file)
@@ -381,8 +381,8 @@ static noinline char *put_dec(char *buf, unsigned long long num)
 #define PLUS   4               /* show plus */
 #define SPACE  8               /* space if plus */
 #define LEFT   16              /* left justified */
-#define SMALL  32              /* Must be 32 == 0x20 */
-#define SPECIAL        64              /* 0x */
+#define SMALL  32              /* use lowercase in hex (must be 32 == 0x20) */
+#define SPECIAL        64              /* prefix hex with "0x", octal with "0" */
 
 enum format_type {
        FORMAT_TYPE_NONE, /* Just a string part */
@@ -408,12 +408,12 @@ enum format_type {
 };
 
 struct printf_spec {
-       enum format_type        type;
-       int                     flags;          /* flags to number() */
-       int                     field_width;    /* width of output field */
-       int                     base;
-       int                     precision;      /* # of digits/chars */
-       int                     qualifier;
+       u16     type;
+       s16     field_width;    /* width of output field */
+       u8      flags;          /* flags to number() */
+       u8      base;
+       s8      precision;      /* # of digits/chars */
+       u8      qualifier;
 };
 
 static char *number(char *buf, char *end, unsigned long long num,
@@ -597,22 +597,29 @@ static char *resource_string(char *buf, char *end, struct resource *res,
 #ifndef MEM_RSRC_PRINTK_SIZE
 #define MEM_RSRC_PRINTK_SIZE   10
 #endif
-       struct printf_spec hex_spec = {
+       static const struct printf_spec io_spec = {
                .base = 16,
+               .field_width = IO_RSRC_PRINTK_SIZE,
                .precision = -1,
                .flags = SPECIAL | SMALL | ZEROPAD,
        };
-       struct printf_spec dec_spec = {
+       static const struct printf_spec mem_spec = {
+               .base = 16,
+               .field_width = MEM_RSRC_PRINTK_SIZE,
+               .precision = -1,
+               .flags = SPECIAL | SMALL | ZEROPAD,
+       };
+       static const struct printf_spec dec_spec = {
                .base = 10,
                .precision = -1,
                .flags = 0,
        };
-       struct printf_spec str_spec = {
+       static const struct printf_spec str_spec = {
                .field_width = -1,
                .precision = 10,
                .flags = LEFT,
        };
-       struct printf_spec flag_spec = {
+       static const struct printf_spec flag_spec = {
                .base = 16,
                .precision = -1,
                .flags = SPECIAL | SMALL,
@@ -628,35 +635,31 @@ static char *resource_string(char *buf, char *end, struct resource *res,
                     2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
 
        char *p = sym, *pend = sym + sizeof(sym);
-       int size = -1, addr = 0;
        int decode = (fmt[0] == 'R') ? 1 : 0;
-
-       if (res->flags & IORESOURCE_IO) {
-               size = IO_RSRC_PRINTK_SIZE;
-               addr = 1;
-       } else if (res->flags & IORESOURCE_MEM) {
-               size = MEM_RSRC_PRINTK_SIZE;
-               addr = 1;
-       }
+       const struct printf_spec *specp;
 
        *p++ = '[';
-       if (res->flags & IORESOURCE_IO)
+       if (res->flags & IORESOURCE_IO) {
                p = string(p, pend, "io  ", str_spec);
-       else if (res->flags & IORESOURCE_MEM)
+               specp = &io_spec;
+       } else if (res->flags & IORESOURCE_MEM) {
                p = string(p, pend, "mem ", str_spec);
-       else if (res->flags & IORESOURCE_IRQ)
+               specp = &mem_spec;
+       } else if (res->flags & IORESOURCE_IRQ) {
                p = string(p, pend, "irq ", str_spec);
-       else if (res->flags & IORESOURCE_DMA)
+               specp = &dec_spec;
+       } else if (res->flags & IORESOURCE_DMA) {
                p = string(p, pend, "dma ", str_spec);
-       else {
+               specp = &dec_spec;
+       } else {
                p = string(p, pend, "??? ", str_spec);
+               specp = &mem_spec;
                decode = 0;
        }
-       hex_spec.field_width = size;
-       p = number(p, pend, res->start, addr ? hex_spec : dec_spec);
+       p = number(p, pend, res->start, *specp);
        if (res->start != res->end) {
                *p++ = '-';
-               p = number(p, pend, res->end, addr ? hex_spec : dec_spec);
+               p = number(p, pend, res->end, *specp);
        }
        if (decode) {
                if (res->flags & IORESOURCE_MEM_64)
@@ -1333,7 +1336,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                        break;
 
                case FORMAT_TYPE_NRCHARS: {
-                       int qualifier = spec.qualifier;
+                       u8 qualifier = spec.qualifier;
 
                        if (qualifier == 'l') {
                                long *ip = va_arg(args, long *);
@@ -1619,7 +1622,7 @@ do {                                                                      \
 
                case FORMAT_TYPE_NRCHARS: {
                        /* skip %n 's argument */
-                       int qualifier = spec.qualifier;
+                       u8 qualifier = spec.qualifier;
                        void *skip_arg;
                        if (qualifier == 'l')
                                skip_arg = va_arg(args, long *);
@@ -1885,7 +1888,9 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
        char *next;
        char digit;
        int num = 0;
-       int qualifier, base, field_width;
+       u8 qualifier;
+       u8 base;
+       s16 field_width;
        bool is_sign;
 
        while (*fmt && *str) {
@@ -1963,7 +1968,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
                {
                        char *s = (char *)va_arg(args, char *);
                        if (field_width == -1)
-                               field_width = INT_MAX;
+                               field_width = SHORT_MAX;
                        /* first, skip leading white space in buffer */
                        str = skip_spaces(str);
 
index e43359214f6ff15020b6f05cc07aa5e6ddcb4162..8d723c9e8b75b316041ea564f5e243b79a94e2b7 100644 (file)
@@ -77,12 +77,20 @@ SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice)
        switch (advice) {
        case POSIX_FADV_NORMAL:
                file->f_ra.ra_pages = bdi->ra_pages;
+               spin_lock(&file->f_lock);
+               file->f_mode &= ~FMODE_RANDOM;
+               spin_unlock(&file->f_lock);
                break;
        case POSIX_FADV_RANDOM:
-               file->f_ra.ra_pages = 0;
+               spin_lock(&file->f_lock);
+               file->f_mode |= FMODE_RANDOM;
+               spin_unlock(&file->f_lock);
                break;
        case POSIX_FADV_SEQUENTIAL:
                file->f_ra.ra_pages = bdi->ra_pages * 2;
+               spin_lock(&file->f_lock);
+               file->f_mode &= ~FMODE_RANDOM;
+               spin_unlock(&file->f_lock);
                break;
        case POSIX_FADV_WILLNEED:
                if (!mapping->a_ops->readpage) {
index 9339de5f0a915dc0526e276eddbcc5fb918fb13d..bb41f98dd8b700d7ecf1765aa7e76e6cbd6ad97a 100644 (file)
@@ -1,18 +1,22 @@
 #include <linux/fault-inject.h>
 #include <linux/gfp.h>
+#include <linux/slab.h>
 
 static struct {
        struct fault_attr attr;
        u32 ignore_gfp_wait;
+       int cache_filter;
 #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
        struct dentry *ignore_gfp_wait_file;
+       struct dentry *cache_filter_file;
 #endif
 } failslab = {
        .attr = FAULT_ATTR_INITIALIZER,
        .ignore_gfp_wait = 1,
+       .cache_filter = 0,
 };
 
-bool should_failslab(size_t size, gfp_t gfpflags)
+bool should_failslab(size_t size, gfp_t gfpflags, unsigned long cache_flags)
 {
        if (gfpflags & __GFP_NOFAIL)
                return false;
@@ -20,6 +24,9 @@ bool should_failslab(size_t size, gfp_t gfpflags)
         if (failslab.ignore_gfp_wait && (gfpflags & __GFP_WAIT))
                return false;
 
+       if (failslab.cache_filter && !(cache_flags & SLAB_FAILSLAB))
+               return false;
+
        return should_fail(&failslab.attr, size);
 }
 
@@ -30,7 +37,6 @@ static int __init setup_failslab(char *str)
 __setup("failslab=", setup_failslab);
 
 #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-
 static int __init failslab_debugfs_init(void)
 {
        mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
@@ -46,8 +52,14 @@ static int __init failslab_debugfs_init(void)
                debugfs_create_bool("ignore-gfp-wait", mode, dir,
                                      &failslab.ignore_gfp_wait);
 
-       if (!failslab.ignore_gfp_wait_file) {
+       failslab.cache_filter_file =
+               debugfs_create_bool("cache-filter", mode, dir,
+                                     &failslab.cache_filter);
+
+       if (!failslab.ignore_gfp_wait_file ||
+           !failslab.cache_filter_file) {
                err = -ENOMEM;
+               debugfs_remove(failslab.cache_filter_file);
                debugfs_remove(failslab.ignore_gfp_wait_file);
                cleanup_fault_attr_dentries(&failslab.attr);
        }
index 698ea80f21022bf709dacc1101c614327109d828..045b31c37653d133394146c8b49db671ea016591 100644 (file)
@@ -1117,7 +1117,7 @@ readpage:
                        if (!PageUptodate(page)) {
                                if (page->mapping == NULL) {
                                        /*
-                                        * invalidate_inode_pages got it
+                                        * invalidate_mapping_pages got it
                                         */
                                        unlock_page(page);
                                        page_cache_release(page);
@@ -1986,7 +1986,7 @@ EXPORT_SYMBOL(iov_iter_single_seg_count);
 inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk)
 {
        struct inode *inode = file->f_mapping->host;
-       unsigned long limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
+       unsigned long limit = rlimit(RLIMIT_FSIZE);
 
         if (unlikely(*pos < 0))
                 return -EINVAL;
index 1888b2d71bb8d78a1a17ec408d1865a7919e76ea..78b94f0b6d5dbd6e95ac1c5cb44937fc45d13e45 100644 (file)
@@ -194,7 +194,7 @@ retry:
                        flush_cache_page(vma, address, pte_pfn(*pte));
                        pteval = ptep_clear_flush_notify(vma, address, pte);
                        page_remove_rmap(page);
-                       dec_mm_counter(mm, file_rss);
+                       dec_mm_counter(mm, MM_FILEPAGES);
                        BUG_ON(pte_dirty(pteval));
                        pte_unmap_unlock(pte, ptl);
                        page_cache_release(page);
index b6ec85abbb39cc22dac7e9c309953c60a22e921c..46f5dacf90a2cd62427fdf89b67fa01ef8af68e0 100644 (file)
@@ -40,7 +40,7 @@ static void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma,
                        page_remove_rmap(page);
                        page_cache_release(page);
                        update_hiwater_rss(mm);
-                       dec_mm_counter(mm, file_rss);
+                       dec_mm_counter(mm, MM_FILEPAGES);
                }
        } else {
                if (!pte_file(pte))
index 56a0da1f9979d7eaa9d85cbc0b20ef76215e4abf..a93f1b7f508cde17cb0b41f7183641dcfdb7a26f 100644 (file)
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -1563,10 +1563,12 @@ int page_referenced_ksm(struct page *page, struct mem_cgroup *memcg,
 again:
        hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
                struct anon_vma *anon_vma = rmap_item->anon_vma;
+               struct anon_vma_chain *vmac;
                struct vm_area_struct *vma;
 
                spin_lock(&anon_vma->lock);
-               list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+               list_for_each_entry(vmac, &anon_vma->head, same_anon_vma) {
+                       vma = vmac->vma;
                        if (rmap_item->address < vma->vm_start ||
                            rmap_item->address >= vma->vm_end)
                                continue;
@@ -1614,10 +1616,12 @@ int try_to_unmap_ksm(struct page *page, enum ttu_flags flags)
 again:
        hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
                struct anon_vma *anon_vma = rmap_item->anon_vma;
+               struct anon_vma_chain *vmac;
                struct vm_area_struct *vma;
 
                spin_lock(&anon_vma->lock);
-               list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+               list_for_each_entry(vmac, &anon_vma->head, same_anon_vma) {
+                       vma = vmac->vma;
                        if (rmap_item->address < vma->vm_start ||
                            rmap_item->address >= vma->vm_end)
                                continue;
@@ -1664,10 +1668,12 @@ int rmap_walk_ksm(struct page *page, int (*rmap_one)(struct page *,
 again:
        hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
                struct anon_vma *anon_vma = rmap_item->anon_vma;
+               struct anon_vma_chain *vmac;
                struct vm_area_struct *vma;
 
                spin_lock(&anon_vma->lock);
-               list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+               list_for_each_entry(vmac, &anon_vma->head, same_anon_vma) {
+                       vma = vmac->vma;
                        if (rmap_item->address < vma->vm_start ||
                            rmap_item->address >= vma->vm_end)
                                continue;
index 954032b80bedd0f761d66ba8c984dc88207c9584..d813823ab08f06916a3d905ecfabffe3d0ca217f 100644 (file)
@@ -2545,7 +2545,7 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *mem,
                pc = list_entry(list->prev, struct page_cgroup, lru);
                if (busy == pc) {
                        list_move(&pc->lru, list);
-                       busy = 0;
+                       busy = NULL;
                        spin_unlock_irqrestore(&zone->lru_lock, flags);
                        continue;
                }
index 17299fd4577c6fc903b5fcb15e52f7fba4f2149d..d1f3351629766ffe112e3f904565ed3035412774 100644 (file)
@@ -383,9 +383,12 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
        if (av == NULL) /* Not actually mapped anymore */
                goto out;
        for_each_process (tsk) {
+               struct anon_vma_chain *vmac;
+
                if (!task_early_kill(tsk))
                        continue;
-               list_for_each_entry (vma, &av->head, anon_vma_node) {
+               list_for_each_entry(vmac, &av->head, same_anon_vma) {
+                       vma = vmac->vma;
                        if (!page_mapped_in_vma(page, vma))
                                continue;
                        if (vma->vm_mm == tsk->mm)
index 72fb5f39bccc32dd17f3aa58f9567f146661301e..d1153e37e9baff33ecc7a6ce26e99133b92b4c66 100644 (file)
@@ -121,6 +121,80 @@ static int __init init_zero_pfn(void)
 }
 core_initcall(init_zero_pfn);
 
+
+#if defined(SPLIT_RSS_COUNTING)
+
+void __sync_task_rss_stat(struct task_struct *task, struct mm_struct *mm)
+{
+       int i;
+
+       for (i = 0; i < NR_MM_COUNTERS; i++) {
+               if (task->rss_stat.count[i]) {
+                       add_mm_counter(mm, i, task->rss_stat.count[i]);
+                       task->rss_stat.count[i] = 0;
+               }
+       }
+       task->rss_stat.events = 0;
+}
+
+static void add_mm_counter_fast(struct mm_struct *mm, int member, int val)
+{
+       struct task_struct *task = current;
+
+       if (likely(task->mm == mm))
+               task->rss_stat.count[member] += val;
+       else
+               add_mm_counter(mm, member, val);
+}
+#define inc_mm_counter_fast(mm, member) add_mm_counter_fast(mm, member, 1)
+#define dec_mm_counter_fast(mm, member) add_mm_counter_fast(mm, member, -1)
+
+/* sync counter once per 64 page faults */
+#define TASK_RSS_EVENTS_THRESH (64)
+static void check_sync_rss_stat(struct task_struct *task)
+{
+       if (unlikely(task != current))
+               return;
+       if (unlikely(task->rss_stat.events++ > TASK_RSS_EVENTS_THRESH))
+               __sync_task_rss_stat(task, task->mm);
+}
+
+unsigned long get_mm_counter(struct mm_struct *mm, int member)
+{
+       long val = 0;
+
+       /*
+        * Don't use task->mm here...for avoiding to use task_get_mm()..
+        * The caller must guarantee task->mm is not invalid.
+        */
+       val = atomic_long_read(&mm->rss_stat.count[member]);
+       /*
+        * counter is updated in asynchronous manner and may go to minus.
+        * But it's never be expected number for users.
+        */
+       if (val < 0)
+               return 0;
+       return (unsigned long)val;
+}
+
+void sync_mm_rss(struct task_struct *task, struct mm_struct *mm)
+{
+       __sync_task_rss_stat(task, mm);
+}
+#else
+
+#define inc_mm_counter_fast(mm, member) inc_mm_counter(mm, member)
+#define dec_mm_counter_fast(mm, member) dec_mm_counter(mm, member)
+
+static void check_sync_rss_stat(struct task_struct *task)
+{
+}
+
+void sync_mm_rss(struct task_struct *task, struct mm_struct *mm)
+{
+}
+#endif
+
 /*
  * If a p?d_bad entry is found while walking page tables, report
  * the error, before resetting entry to p?d_none.  Usually (but
@@ -300,7 +374,7 @@ void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *vma,
                 * Hide vma from rmap and truncate_pagecache before freeing
                 * pgtables
                 */
-               anon_vma_unlink(vma);
+               unlink_anon_vmas(vma);
                unlink_file_vma(vma);
 
                if (is_vm_hugetlb_page(vma)) {
@@ -314,7 +388,7 @@ void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *vma,
                               && !is_vm_hugetlb_page(next)) {
                                vma = next;
                                next = vma->vm_next;
-                               anon_vma_unlink(vma);
+                               unlink_anon_vmas(vma);
                                unlink_file_vma(vma);
                        }
                        free_pgd_range(tlb, addr, vma->vm_end,
@@ -376,12 +450,20 @@ int __pte_alloc_kernel(pmd_t *pmd, unsigned long address)
        return 0;
 }
 
-static inline void add_mm_rss(struct mm_struct *mm, int file_rss, int anon_rss)
+static inline void init_rss_vec(int *rss)
 {
-       if (file_rss)
-               add_mm_counter(mm, file_rss, file_rss);
-       if (anon_rss)
-               add_mm_counter(mm, anon_rss, anon_rss);
+       memset(rss, 0, sizeof(int) * NR_MM_COUNTERS);
+}
+
+static inline void add_mm_rss_vec(struct mm_struct *mm, int *rss)
+{
+       int i;
+
+       if (current->mm == mm)
+               sync_mm_rss(current, mm);
+       for (i = 0; i < NR_MM_COUNTERS; i++)
+               if (rss[i])
+                       add_mm_counter(mm, i, rss[i]);
 }
 
 /*
@@ -597,7 +679,9 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                                                 &src_mm->mmlist);
                                spin_unlock(&mmlist_lock);
                        }
-                       if (is_write_migration_entry(entry) &&
+                       if (likely(!non_swap_entry(entry)))
+                               rss[MM_SWAPENTS]++;
+                       else if (is_write_migration_entry(entry) &&
                                        is_cow_mapping(vm_flags)) {
                                /*
                                 * COW mappings require pages in both parent
@@ -632,7 +716,10 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        if (page) {
                get_page(page);
                page_dup_rmap(page);
-               rss[PageAnon(page)]++;
+               if (PageAnon(page))
+                       rss[MM_ANONPAGES]++;
+               else
+                       rss[MM_FILEPAGES]++;
        }
 
 out_set_pte:
@@ -648,11 +735,12 @@ static int copy_pte_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
        pte_t *src_pte, *dst_pte;
        spinlock_t *src_ptl, *dst_ptl;
        int progress = 0;
-       int rss[2];
+       int rss[NR_MM_COUNTERS];
        swp_entry_t entry = (swp_entry_t){0};
 
 again:
-       rss[1] = rss[0] = 0;
+       init_rss_vec(rss);
+
        dst_pte = pte_alloc_map_lock(dst_mm, dst_pmd, addr, &dst_ptl);
        if (!dst_pte)
                return -ENOMEM;
@@ -688,7 +776,7 @@ again:
        arch_leave_lazy_mmu_mode();
        spin_unlock(src_ptl);
        pte_unmap_nested(orig_src_pte);
-       add_mm_rss(dst_mm, rss[0], rss[1]);
+       add_mm_rss_vec(dst_mm, rss);
        pte_unmap_unlock(orig_dst_pte, dst_ptl);
        cond_resched();
 
@@ -816,8 +904,9 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
        struct mm_struct *mm = tlb->mm;
        pte_t *pte;
        spinlock_t *ptl;
-       int file_rss = 0;
-       int anon_rss = 0;
+       int rss[NR_MM_COUNTERS];
+
+       init_rss_vec(rss);
 
        pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
        arch_enter_lazy_mmu_mode();
@@ -863,14 +952,14 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
                                set_pte_at(mm, addr, pte,
                                           pgoff_to_pte(page->index));
                        if (PageAnon(page))
-                               anon_rss--;
+                               rss[MM_ANONPAGES]--;
                        else {
                                if (pte_dirty(ptent))
                                        set_page_dirty(page);
                                if (pte_young(ptent) &&
                                    likely(!VM_SequentialReadHint(vma)))
                                        mark_page_accessed(page);
-                               file_rss--;
+                               rss[MM_FILEPAGES]--;
                        }
                        page_remove_rmap(page);
                        if (unlikely(page_mapcount(page) < 0))
@@ -887,13 +976,18 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
                if (pte_file(ptent)) {
                        if (unlikely(!(vma->vm_flags & VM_NONLINEAR)))
                                print_bad_pte(vma, addr, ptent, NULL);
-               } else if
-                 (unlikely(!free_swap_and_cache(pte_to_swp_entry(ptent))))
-                       print_bad_pte(vma, addr, ptent, NULL);
+               } else {
+                       swp_entry_t entry = pte_to_swp_entry(ptent);
+
+                       if (!non_swap_entry(entry))
+                               rss[MM_SWAPENTS]--;
+                       if (unlikely(!free_swap_and_cache(entry)))
+                               print_bad_pte(vma, addr, ptent, NULL);
+               }
                pte_clear_not_present_full(mm, addr, pte, tlb->fullmm);
        } while (pte++, addr += PAGE_SIZE, (addr != end && *zap_work > 0));
 
-       add_mm_rss(mm, file_rss, anon_rss);
+       add_mm_rss_vec(mm, rss);
        arch_leave_lazy_mmu_mode();
        pte_unmap_unlock(pte - 1, ptl);
 
@@ -1527,7 +1621,7 @@ static int insert_page(struct vm_area_struct *vma, unsigned long addr,
 
        /* Ok, finally just insert the thing.. */
        get_page(page);
-       inc_mm_counter(mm, file_rss);
+       inc_mm_counter_fast(mm, MM_FILEPAGES);
        page_add_file_rmap(page);
        set_pte_at(mm, addr, pte, mk_pte(page, prot));
 
@@ -2044,6 +2138,13 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                        page_cache_release(old_page);
                }
                reuse = reuse_swap_page(old_page);
+               if (reuse)
+                       /*
+                        * The page is all ours.  Move it to our anon_vma so
+                        * the rmap code will not search our parent or siblings.
+                        * Protected against the rmap code by the page lock.
+                        */
+                       page_move_anon_rmap(old_page, vma, address);
                unlock_page(old_page);
        } else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) ==
                                        (VM_WRITE|VM_SHARED))) {
@@ -2163,11 +2264,11 @@ gotten:
        if (likely(pte_same(*page_table, orig_pte))) {
                if (old_page) {
                        if (!PageAnon(old_page)) {
-                               dec_mm_counter(mm, file_rss);
-                               inc_mm_counter(mm, anon_rss);
+                               dec_mm_counter_fast(mm, MM_FILEPAGES);
+                               inc_mm_counter_fast(mm, MM_ANONPAGES);
                        }
                } else
-                       inc_mm_counter(mm, anon_rss);
+                       inc_mm_counter_fast(mm, MM_ANONPAGES);
                flush_cache_page(vma, address, pte_pfn(orig_pte));
                entry = mk_pte(new_page, vma->vm_page_prot);
                entry = maybe_mkwrite(pte_mkdirty(entry), vma);
@@ -2604,7 +2705,8 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
         * discarded at swap_free().
         */
 
-       inc_mm_counter(mm, anon_rss);
+       inc_mm_counter_fast(mm, MM_ANONPAGES);
+       dec_mm_counter_fast(mm, MM_SWAPENTS);
        pte = mk_pte(page, vma->vm_page_prot);
        if ((flags & FAULT_FLAG_WRITE) && reuse_swap_page(page)) {
                pte = maybe_mkwrite(pte_mkdirty(pte), vma);
@@ -2688,7 +2790,7 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,
        if (!pte_none(*page_table))
                goto release;
 
-       inc_mm_counter(mm, anon_rss);
+       inc_mm_counter_fast(mm, MM_ANONPAGES);
        page_add_new_anon_rmap(page, vma, address);
 setpte:
        set_pte_at(mm, address, page_table, entry);
@@ -2842,10 +2944,10 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
                if (flags & FAULT_FLAG_WRITE)
                        entry = maybe_mkwrite(pte_mkdirty(entry), vma);
                if (anon) {
-                       inc_mm_counter(mm, anon_rss);
+                       inc_mm_counter_fast(mm, MM_ANONPAGES);
                        page_add_new_anon_rmap(page, vma, address);
                } else {
-                       inc_mm_counter(mm, file_rss);
+                       inc_mm_counter_fast(mm, MM_FILEPAGES);
                        page_add_file_rmap(page);
                        if (flags & FAULT_FLAG_WRITE) {
                                dirty_page = page;
@@ -3023,6 +3125,9 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 
        count_vm_event(PGFAULT);
 
+       /* do counter updates before entering really critical section. */
+       check_sync_rss_stat(current);
+
        if (unlikely(is_vm_hugetlb_page(vma)))
                return hugetlb_fault(mm, vma, address, flags);
 
index 030ce8a5bb0e758e7ea04cc0c851955275356426..78e34e63c7b880a03d3b7e043def593f81a65ac5 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/mm_inline.h>
+#include <linux/firmware-map.h>
 
 #include <asm/tlbflush.h>
 
@@ -523,6 +524,9 @@ int __ref add_memory(int nid, u64 start, u64 size)
                BUG_ON(ret);
        }
 
+       /* create new memmap entry */
+       firmware_map_add_hotplug(start, start + size, "System RAM");
+
        goto out;
 
 error:
index 290fb5bf0440f7a5e0f05f4adeffd3decb785d00..bda230e52acd94b0475640c4fd6b97895c6d3b9a 100644 (file)
@@ -563,24 +563,50 @@ static int policy_vma(struct vm_area_struct *vma, struct mempolicy *new)
 }
 
 /* Step 2: apply policy to a range and do splits. */
-static int mbind_range(struct vm_area_struct *vma, unsigned long start,
-                      unsigned long end, struct mempolicy *new)
+static int mbind_range(struct mm_struct *mm, unsigned long start,
+                      unsigned long end, struct mempolicy *new_pol)
 {
        struct vm_area_struct *next;
-       int err;
+       struct vm_area_struct *prev;
+       struct vm_area_struct *vma;
+       int err = 0;
+       pgoff_t pgoff;
+       unsigned long vmstart;
+       unsigned long vmend;
 
-       err = 0;
-       for (; vma && vma->vm_start < end; vma = next) {
+       vma = find_vma_prev(mm, start, &prev);
+       if (!vma || vma->vm_start > start)
+               return -EFAULT;
+
+       for (; vma && vma->vm_start < end; prev = vma, vma = next) {
                next = vma->vm_next;
-               if (vma->vm_start < start)
-                       err = split_vma(vma->vm_mm, vma, start, 1);
-               if (!err && vma->vm_end > end)
-                       err = split_vma(vma->vm_mm, vma, end, 0);
-               if (!err)
-                       err = policy_vma(vma, new);
+               vmstart = max(start, vma->vm_start);
+               vmend   = min(end, vma->vm_end);
+
+               pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
+               prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags,
+                                 vma->anon_vma, vma->vm_file, pgoff, new_pol);
+               if (prev) {
+                       vma = prev;
+                       next = vma->vm_next;
+                       continue;
+               }
+               if (vma->vm_start != vmstart) {
+                       err = split_vma(vma->vm_mm, vma, vmstart, 1);
+                       if (err)
+                               goto out;
+               }
+               if (vma->vm_end != vmend) {
+                       err = split_vma(vma->vm_mm, vma, vmend, 0);
+                       if (err)
+                               goto out;
+               }
+               err = policy_vma(vma, new_pol);
                if (err)
-                       break;
+                       goto out;
        }
+
+ out:
        return err;
 }
 
@@ -862,36 +888,36 @@ int do_migrate_pages(struct mm_struct *mm,
        if (err)
                goto out;
 
-/*
- * Find a 'source' bit set in 'tmp' whose corresponding 'dest'
- * bit in 'to' is not also set in 'tmp'.  Clear the found 'source'
- * bit in 'tmp', and return that <source, dest> pair for migration.
- * The pair of nodemasks 'to' and 'from' define the map.
- *
- * If no pair of bits is found that way, fallback to picking some
- * pair of 'source' and 'dest' bits that are not the same.  If the
- * 'source' and 'dest' bits are the same, this represents a node
- * that will be migrating to itself, so no pages need move.
- *
- * If no bits are left in 'tmp', or if all remaining bits left
- * in 'tmp' correspond to the same bit in 'to', return false
- * (nothing left to migrate).
- *
- * This lets us pick a pair of nodes to migrate between, such that
- * if possible the dest node is not already occupied by some other
- * source node, minimizing the risk of overloading the memory on a
- * node that would happen if we migrated incoming memory to a node
- * before migrating outgoing memory source that same node.
- *
- * A single scan of tmp is sufficient.  As we go, we remember the
- * most recent <s, d> pair that moved (s != d).  If we find a pair
- * that not only moved, but what's better, moved to an empty slot
- * (d is not set in tmp), then we break out then, with that pair.
- * Otherwise when we finish scannng from_tmp, we at least have the
- * most recent <s, d> pair that moved.  If we get all the way through
- * the scan of tmp without finding any node that moved, much less
- * moved to an empty node, then there is nothing left worth migrating.
- */
+       /*
       * Find a 'source' bit set in 'tmp' whose corresponding 'dest'
       * bit in 'to' is not also set in 'tmp'.  Clear the found 'source'
       * bit in 'tmp', and return that <source, dest> pair for migration.
       * The pair of nodemasks 'to' and 'from' define the map.
       *
       * If no pair of bits is found that way, fallback to picking some
       * pair of 'source' and 'dest' bits that are not the same.  If the
       * 'source' and 'dest' bits are the same, this represents a node
       * that will be migrating to itself, so no pages need move.
       *
       * If no bits are left in 'tmp', or if all remaining bits left
       * in 'tmp' correspond to the same bit in 'to', return false
       * (nothing left to migrate).
       *
       * This lets us pick a pair of nodes to migrate between, such that
       * if possible the dest node is not already occupied by some other
       * source node, minimizing the risk of overloading the memory on a
       * node that would happen if we migrated incoming memory to a node
       * before migrating outgoing memory source that same node.
       *
       * A single scan of tmp is sufficient.  As we go, we remember the
       * most recent <s, d> pair that moved (s != d).  If we find a pair
       * that not only moved, but what's better, moved to an empty slot
       * (d is not set in tmp), then we break out then, with that pair.
       * Otherwise when we finish scannng from_tmp, we at least have the
       * most recent <s, d> pair that moved.  If we get all the way through
       * the scan of tmp without finding any node that moved, much less
       * moved to an empty node, then there is nothing left worth migrating.
       */
 
        tmp = *from_nodes;
        while (!nodes_empty(tmp)) {
@@ -1047,7 +1073,7 @@ static long do_mbind(unsigned long start, unsigned long len,
        if (!IS_ERR(vma)) {
                int nr_failed = 0;
 
-               err = mbind_range(vma, start, end, new);
+               err = mbind_range(mm, start, end, new);
 
                if (!list_empty(&pagelist))
                        nr_failed = migrate_pages(&pagelist, new_vma_page,
index edb6101ed774da0244cb129f332a6914b73465de..88000b89fc9a567768509926b7baf5dc44d2d7d7 100644 (file)
@@ -275,8 +275,6 @@ static int migrate_page_move_mapping(struct address_space *mapping,
  */
 static void migrate_page_copy(struct page *newpage, struct page *page)
 {
-       int anon;
-
        copy_highpage(newpage, page);
 
        if (PageError(page))
@@ -313,8 +311,6 @@ static void migrate_page_copy(struct page *newpage, struct page *page)
        ClearPageSwapCache(page);
        ClearPagePrivate(page);
        set_page_private(page, 0);
-       /* page->mapping contains a flag for PageAnon() */
-       anon = PageAnon(page);
        page->mapping = NULL;
 
        /*
index 2b8335a8940052874a18a9b3657c830c61e13284..8f4e2dfceec1c88e796a6bafb78e7422f06bdb72 100644 (file)
@@ -25,7 +25,7 @@ int can_do_mlock(void)
 {
        if (capable(CAP_IPC_LOCK))
                return 1;
-       if (current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur != 0)
+       if (rlimit(RLIMIT_MEMLOCK) != 0)
                return 1;
        return 0;
 }
@@ -487,7 +487,7 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
        locked = len >> PAGE_SHIFT;
        locked += current->mm->locked_vm;
 
-       lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+       lock_limit = rlimit(RLIMIT_MEMLOCK);
        lock_limit >>= PAGE_SHIFT;
 
        /* check against resource limits */
@@ -550,7 +550,7 @@ SYSCALL_DEFINE1(mlockall, int, flags)
 
        down_write(&current->mm->mmap_sem);
 
-       lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+       lock_limit = rlimit(RLIMIT_MEMLOCK);
        lock_limit >>= PAGE_SHIFT;
 
        ret = -ENOMEM;
@@ -584,7 +584,7 @@ int user_shm_lock(size_t size, struct user_struct *user)
        int allowed = 0;
 
        locked = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+       lock_limit = rlimit(RLIMIT_MEMLOCK);
        if (lock_limit == RLIM_INFINITY)
                allowed = 1;
        lock_limit >>= PAGE_SHIFT;
@@ -618,12 +618,12 @@ int account_locked_memory(struct mm_struct *mm, struct rlimit *rlim,
 
        down_write(&mm->mmap_sem);
 
-       lim = rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT;
+       lim = ACCESS_ONCE(rlim[RLIMIT_AS].rlim_cur) >> PAGE_SHIFT;
        vm   = mm->total_vm + pgsz;
        if (lim < vm)
                goto out;
 
-       lim = rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT;
+       lim = ACCESS_ONCE(rlim[RLIMIT_MEMLOCK].rlim_cur) >> PAGE_SHIFT;
        vm   = mm->locked_vm + pgsz;
        if (lim < vm)
                goto out;
index ee2298936fe677870ad4979b98bd8d6ada7c3439..f1b4448626bf2698ed6cdb9b641b8e12aad21130 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -265,7 +265,7 @@ SYSCALL_DEFINE1(brk, unsigned long, brk)
         * segment grow beyond its set limit the in case where the limit is
         * not page aligned -Ram Gupta
         */
-       rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
+       rlim = rlimit(RLIMIT_DATA);
        if (rlim < RLIM_INFINITY && (brk - mm->start_brk) +
                        (mm->end_data - mm->start_data) > rlim)
                goto out;
@@ -437,7 +437,6 @@ __vma_link(struct mm_struct *mm, struct vm_area_struct *vma,
 {
        __vma_link_list(mm, vma, prev, rb_parent);
        __vma_link_rb(mm, vma, rb_link, rb_parent);
-       __anon_vma_link(vma);
 }
 
 static void vma_link(struct mm_struct *mm, struct vm_area_struct *vma,
@@ -499,7 +498,7 @@ __vma_unlink(struct mm_struct *mm, struct vm_area_struct *vma,
  * are necessary.  The "insert" vma (if any) is to be inserted
  * before we drop the necessary locks.
  */
-void vma_adjust(struct vm_area_struct *vma, unsigned long start,
+int vma_adjust(struct vm_area_struct *vma, unsigned long start,
        unsigned long end, pgoff_t pgoff, struct vm_area_struct *insert)
 {
        struct mm_struct *mm = vma->vm_mm;
@@ -542,6 +541,26 @@ again:                     remove_next = 1 + (end > next->vm_end);
                }
        }
 
+       /*
+        * When changing only vma->vm_end, we don't really need anon_vma lock.
+        */
+       if (vma->anon_vma && (insert || importer || start != vma->vm_start))
+               anon_vma = vma->anon_vma;
+       if (anon_vma) {
+               /*
+                * Easily overlooked: when mprotect shifts the boundary,
+                * make sure the expanding vma has anon_vma set if the
+                * shrinking vma had, to cover any anon pages imported.
+                */
+               if (importer && !importer->anon_vma) {
+                       /* Block reverse map lookups until things are set up. */
+                       if (anon_vma_clone(importer, vma)) {
+                               return -ENOMEM;
+                       }
+                       importer->anon_vma = anon_vma;
+               }
+       }
+
        if (file) {
                mapping = file->f_mapping;
                if (!(vma->vm_flags & VM_NONLINEAR))
@@ -567,25 +586,6 @@ again:                     remove_next = 1 + (end > next->vm_end);
                }
        }
 
-       /*
-        * When changing only vma->vm_end, we don't really need
-        * anon_vma lock.
-        */
-       if (vma->anon_vma && (insert || importer || start != vma->vm_start))
-               anon_vma = vma->anon_vma;
-       if (anon_vma) {
-               spin_lock(&anon_vma->lock);
-               /*
-                * Easily overlooked: when mprotect shifts the boundary,
-                * make sure the expanding vma has anon_vma set if the
-                * shrinking vma had, to cover any anon pages imported.
-                */
-               if (importer && !importer->anon_vma) {
-                       importer->anon_vma = anon_vma;
-                       __anon_vma_link(importer);
-               }
-       }
-
        if (root) {
                flush_dcache_mmap_lock(mapping);
                vma_prio_tree_remove(vma, root);
@@ -616,8 +616,6 @@ again:                      remove_next = 1 + (end > next->vm_end);
                __vma_unlink(mm, next, vma);
                if (file)
                        __remove_shared_vm_struct(next, file, mapping);
-               if (next->anon_vma)
-                       __anon_vma_merge(vma, next);
        } else if (insert) {
                /*
                 * split_vma has split insert from vma, and needs
@@ -627,8 +625,6 @@ again:                      remove_next = 1 + (end > next->vm_end);
                __insert_vm_struct(mm, insert);
        }
 
-       if (anon_vma)
-               spin_unlock(&anon_vma->lock);
        if (mapping)
                spin_unlock(&mapping->i_mmap_lock);
 
@@ -638,6 +634,8 @@ again:                      remove_next = 1 + (end > next->vm_end);
                        if (next->vm_flags & VM_EXECUTABLE)
                                removed_exe_file_vma(mm);
                }
+               if (next->anon_vma)
+                       anon_vma_merge(vma, next);
                mm->map_count--;
                mpol_put(vma_policy(next));
                kmem_cache_free(vm_area_cachep, next);
@@ -653,6 +651,8 @@ again:                      remove_next = 1 + (end > next->vm_end);
        }
 
        validate_mm(mm);
+
+       return 0;
 }
 
 /*
@@ -759,6 +759,7 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
 {
        pgoff_t pglen = (end - addr) >> PAGE_SHIFT;
        struct vm_area_struct *area, *next;
+       int err;
 
        /*
         * We later require that vma->vm_flags == vm_flags,
@@ -792,11 +793,13 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
                                is_mergeable_anon_vma(prev->anon_vma,
                                                      next->anon_vma)) {
                                                        /* cases 1, 6 */
-                       vma_adjust(prev, prev->vm_start,
+                       err = vma_adjust(prev, prev->vm_start,
                                next->vm_end, prev->vm_pgoff, NULL);
                } else                                  /* cases 2, 5, 7 */
-                       vma_adjust(prev, prev->vm_start,
+                       err = vma_adjust(prev, prev->vm_start,
                                end, prev->vm_pgoff, NULL);
+               if (err)
+                       return NULL;
                return prev;
        }
 
@@ -808,11 +811,13 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
                        can_vma_merge_before(next, vm_flags,
                                        anon_vma, file, pgoff+pglen)) {
                if (prev && addr < prev->vm_end)        /* case 4 */
-                       vma_adjust(prev, prev->vm_start,
+                       err = vma_adjust(prev, prev->vm_start,
                                addr, prev->vm_pgoff, NULL);
                else                                    /* cases 3, 8 */
-                       vma_adjust(area, addr, next->vm_end,
+                       err = vma_adjust(area, addr, next->vm_end,
                                next->vm_pgoff - pglen, NULL);
+               if (err)
+                       return NULL;
                return area;
        }
 
@@ -967,7 +972,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                unsigned long locked, lock_limit;
                locked = len >> PAGE_SHIFT;
                locked += mm->locked_vm;
-               lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+               lock_limit = rlimit(RLIMIT_MEMLOCK);
                lock_limit >>= PAGE_SHIFT;
                if (locked > lock_limit && !capable(CAP_IPC_LOCK))
                        return -EAGAIN;
@@ -1205,6 +1210,7 @@ munmap_back:
        vma->vm_flags = vm_flags;
        vma->vm_page_prot = vm_get_page_prot(vm_flags);
        vma->vm_pgoff = pgoff;
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
 
        if (file) {
                error = -EINVAL;
@@ -1265,13 +1271,8 @@ out:
        mm->total_vm += len >> PAGE_SHIFT;
        vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
        if (vm_flags & VM_LOCKED) {
-               /*
-                * makes pages present; downgrades, drops, reacquires mmap_sem
-                */
-               long nr_pages = mlock_vma_pages_range(vma, addr, addr + len);
-               if (nr_pages < 0)
-                       return nr_pages;        /* vma gone! */
-               mm->locked_vm += (len >> PAGE_SHIFT) - nr_pages;
+               if (!mlock_vma_pages_range(vma, addr, addr + len))
+                       mm->locked_vm += (len >> PAGE_SHIFT);
        } else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK))
                make_pages_present(addr, addr + len);
        return addr;
@@ -1599,7 +1600,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
                return -ENOMEM;
 
        /* Stack limit test */
-       if (size > rlim[RLIMIT_STACK].rlim_cur)
+       if (size > ACCESS_ONCE(rlim[RLIMIT_STACK].rlim_cur))
                return -ENOMEM;
 
        /* mlock limit tests */
@@ -1607,7 +1608,8 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
                unsigned long locked;
                unsigned long limit;
                locked = mm->locked_vm + grow;
-               limit = rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT;
+               limit = ACCESS_ONCE(rlim[RLIMIT_MEMLOCK].rlim_cur);
+               limit >>= PAGE_SHIFT;
                if (locked > limit && !capable(CAP_IPC_LOCK))
                        return -ENOMEM;
        }
@@ -1754,8 +1756,7 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr)
        if (!prev || expand_stack(prev, addr))
                return NULL;
        if (prev->vm_flags & VM_LOCKED) {
-               if (mlock_vma_pages_range(prev, addr, prev->vm_end) < 0)
-                       return NULL;    /* vma gone! */
+               mlock_vma_pages_range(prev, addr, prev->vm_end);
        }
        return prev;
 }
@@ -1783,8 +1784,7 @@ find_extend_vma(struct mm_struct * mm, unsigned long addr)
        if (expand_stack(vma, addr))
                return NULL;
        if (vma->vm_flags & VM_LOCKED) {
-               if (mlock_vma_pages_range(vma, addr, start) < 0)
-                       return NULL;    /* vma gone! */
+               mlock_vma_pages_range(vma, addr, start);
        }
        return vma;
 }
@@ -1871,6 +1871,7 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
 {
        struct mempolicy *pol;
        struct vm_area_struct *new;
+       int err = -ENOMEM;
 
        if (is_vm_hugetlb_page(vma) && (addr &
                                        ~(huge_page_mask(hstate_vma(vma)))))
@@ -1878,11 +1879,13 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
 
        new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);
        if (!new)
-               return -ENOMEM;
+               goto out_err;
 
        /* most fields are the same, copy all, and then fixup */
        *new = *vma;
 
+       INIT_LIST_HEAD(&new->anon_vma_chain);
+
        if (new_below)
                new->vm_end = addr;
        else {
@@ -1892,11 +1895,14 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
 
        pol = mpol_dup(vma_policy(vma));
        if (IS_ERR(pol)) {
-               kmem_cache_free(vm_area_cachep, new);
-               return PTR_ERR(pol);
+               err = PTR_ERR(pol);
+               goto out_free_vma;
        }
        vma_set_policy(new, pol);
 
+       if (anon_vma_clone(new, vma))
+               goto out_free_mpol;
+
        if (new->vm_file) {
                get_file(new->vm_file);
                if (vma->vm_flags & VM_EXECUTABLE)
@@ -1907,12 +1913,28 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
                new->vm_ops->open(new);
 
        if (new_below)
-               vma_adjust(vma, addr, vma->vm_end, vma->vm_pgoff +
+               err = vma_adjust(vma, addr, vma->vm_end, vma->vm_pgoff +
                        ((addr - new->vm_start) >> PAGE_SHIFT), new);
        else
-               vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);
+               err = vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);
 
-       return 0;
+       /* Success. */
+       if (!err)
+               return 0;
+
+       /* Clean everything up if vma_adjust failed. */
+       new->vm_ops->close(new);
+       if (new->vm_file) {
+               if (vma->vm_flags & VM_EXECUTABLE)
+                       removed_exe_file_vma(mm);
+               fput(new->vm_file);
+       }
+ out_free_mpol:
+       mpol_put(pol);
+ out_free_vma:
+       kmem_cache_free(vm_area_cachep, new);
+ out_err:
+       return err;
 }
 
 /*
@@ -2074,7 +2096,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
                unsigned long locked, lock_limit;
                locked = len >> PAGE_SHIFT;
                locked += mm->locked_vm;
-               lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+               lock_limit = rlimit(RLIMIT_MEMLOCK);
                lock_limit >>= PAGE_SHIFT;
                if (locked > lock_limit && !capable(CAP_IPC_LOCK))
                        return -EAGAIN;
@@ -2122,6 +2144,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
                return -ENOMEM;
        }
 
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
@@ -2258,10 +2281,11 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
                if (new_vma) {
                        *new_vma = *vma;
                        pol = mpol_dup(vma_policy(vma));
-                       if (IS_ERR(pol)) {
-                               kmem_cache_free(vm_area_cachep, new_vma);
-                               return NULL;
-                       }
+                       if (IS_ERR(pol))
+                               goto out_free_vma;
+                       INIT_LIST_HEAD(&new_vma->anon_vma_chain);
+                       if (anon_vma_clone(new_vma, vma))
+                               goto out_free_mempol;
                        vma_set_policy(new_vma, pol);
                        new_vma->vm_start = addr;
                        new_vma->vm_end = addr + len;
@@ -2277,6 +2301,12 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
                }
        }
        return new_vma;
+
+ out_free_mempol:
+       mpol_put(pol);
+ out_free_vma:
+       kmem_cache_free(vm_area_cachep, new_vma);
+       return NULL;
 }
 
 /*
@@ -2288,7 +2318,7 @@ int may_expand_vm(struct mm_struct *mm, unsigned long npages)
        unsigned long cur = mm->total_vm;       /* pages */
        unsigned long lim;
 
-       lim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT;
+       lim = rlimit(RLIMIT_AS) >> PAGE_SHIFT;
 
        if (cur + npages > lim)
                return 0;
@@ -2354,6 +2384,7 @@ int install_special_mapping(struct mm_struct *mm,
        if (unlikely(vma == NULL))
                return -ENOMEM;
 
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
        vma->vm_mm = mm;
        vma->vm_start = addr;
        vma->vm_end = addr + len;
@@ -2454,6 +2485,7 @@ static void vm_lock_mapping(struct mm_struct *mm, struct address_space *mapping)
 int mm_take_all_locks(struct mm_struct *mm)
 {
        struct vm_area_struct *vma;
+       struct anon_vma_chain *avc;
        int ret = -EINTR;
 
        BUG_ON(down_read_trylock(&mm->mmap_sem));
@@ -2471,7 +2503,8 @@ int mm_take_all_locks(struct mm_struct *mm)
                if (signal_pending(current))
                        goto out_unlock;
                if (vma->anon_vma)
-                       vm_lock_anon_vma(mm, vma->anon_vma);
+                       list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
+                               vm_lock_anon_vma(mm, avc->anon_vma);
        }
 
        ret = 0;
@@ -2526,13 +2559,15 @@ static void vm_unlock_mapping(struct address_space *mapping)
 void mm_drop_all_locks(struct mm_struct *mm)
 {
        struct vm_area_struct *vma;
+       struct anon_vma_chain *avc;
 
        BUG_ON(down_read_trylock(&mm->mmap_sem));
        BUG_ON(!mutex_is_locked(&mm_all_locks_mutex));
 
        for (vma = mm->mmap; vma; vma = vma->vm_next) {
                if (vma->anon_vma)
-                       vm_unlock_anon_vma(vma->anon_vma);
+                       list_for_each_entry(avc, &vma->anon_vma_chain, same_vma)
+                               vm_unlock_anon_vma(avc->anon_vma);
                if (vma->vm_file && vma->vm_file->f_mapping)
                        vm_unlock_mapping(vma->vm_file->f_mapping);
        }
index 845190898d59f206201b4b5d60f1d4534a755913..e9c75efce60938a82821d24dedf0702ea5d0a50b 100644 (file)
@@ -285,7 +285,7 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr,
        if (vma->vm_flags & VM_LOCKED) {
                unsigned long locked, lock_limit;
                locked = mm->locked_vm << PAGE_SHIFT;
-               lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+               lock_limit = rlimit(RLIMIT_MEMLOCK);
                locked += new_len - old_len;
                if (locked > lock_limit && !capable(CAP_IPC_LOCK))
                        goto Eagain;
@@ -460,8 +460,11 @@ unsigned long do_mremap(unsigned long addr,
                if (vma_expandable(vma, new_len - old_len)) {
                        int pages = (new_len - old_len) >> PAGE_SHIFT;
 
-                       vma_adjust(vma, vma->vm_start,
-                               addr + new_len, vma->vm_pgoff, NULL);
+                       if (vma_adjust(vma, vma->vm_start, addr + new_len,
+                                      vma->vm_pgoff, NULL)) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
 
                        mm->total_vm += pages;
                        vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages);
index 48a2ecfaf05947230d32db3f54c49a3b0ba58b1d..b9b5cceb1b68261b3c88760a6e0a56d1d626818c 100644 (file)
@@ -146,7 +146,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                        (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
 
        for (i = 0; i < nr_pages; i++) {
-               vma = find_vma(mm, start);
+               vma = find_extend_vma(mm, start);
                if (!vma)
                        goto finish_or_fault;
 
@@ -764,7 +764,7 @@ EXPORT_SYMBOL(find_vma);
  */
 struct vm_area_struct *find_extend_vma(struct mm_struct *mm, unsigned long addr)
 {
-       return find_vma(mm, addr);
+       return find_vma(mm, addr & PAGE_MASK);
 }
 
 /*
@@ -1209,7 +1209,7 @@ unsigned long do_mmap_pgoff(struct file *file,
        region->vm_flags = vm_flags;
        region->vm_pgoff = pgoff;
 
-       INIT_LIST_HEAD(&vma->anon_vma_node);
+       INIT_LIST_HEAD(&vma->anon_vma_chain);
        vma->vm_flags = vm_flags;
        vma->vm_pgoff = pgoff;
 
index 237050478f28f46fc59c5c9984337f1c3d60de08..35755a4156d6db752424311d16a63985eec499cf 100644 (file)
@@ -401,8 +401,8 @@ static void __oom_kill_task(struct task_struct *p, int verbose)
                       "vsz:%lukB, anon-rss:%lukB, file-rss:%lukB\n",
                       task_pid_nr(p), p->comm,
                       K(p->mm->total_vm),
-                      K(get_mm_counter(p->mm, anon_rss)),
-                      K(get_mm_counter(p->mm, file_rss)));
+                      K(get_mm_counter(p->mm, MM_ANONPAGES)),
+                      K(get_mm_counter(p->mm, MM_FILEPAGES)));
        task_unlock(p);
 
        /*
index a6b17aa4740b068feef786b87fe52b198e277375..a8182c89de5920b5eb91809676ee884d89bbfcaf 100644 (file)
@@ -76,6 +76,31 @@ unsigned long totalreserve_pages __read_mostly;
 int percpu_pagelist_fraction;
 gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;
 
+#ifdef CONFIG_PM_SLEEP
+/*
+ * The following functions are used by the suspend/hibernate code to temporarily
+ * change gfp_allowed_mask in order to avoid using I/O during memory allocations
+ * while devices are suspended.  To avoid races with the suspend/hibernate code,
+ * they should always be called with pm_mutex held (gfp_allowed_mask also should
+ * only be modified with pm_mutex held, unless the suspend/hibernate code is
+ * guaranteed not to run in parallel with that modification).
+ */
+void set_gfp_allowed_mask(gfp_t mask)
+{
+       WARN_ON(!mutex_is_locked(&pm_mutex));
+       gfp_allowed_mask = mask;
+}
+
+gfp_t clear_gfp_allowed_mask(gfp_t mask)
+{
+       gfp_t ret = gfp_allowed_mask;
+
+       WARN_ON(!mutex_is_locked(&pm_mutex));
+       gfp_allowed_mask &= ~mask;
+       return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
 #ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
 int pageblock_order __read_mostly;
 #endif
@@ -530,7 +555,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
        int batch_free = 0;
 
        spin_lock(&zone->lock);
-       zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);
+       zone->all_unreclaimable = 0;
        zone->pages_scanned = 0;
 
        __mod_zone_page_state(zone, NR_FREE_PAGES, count);
@@ -568,7 +593,7 @@ static void free_one_page(struct zone *zone, struct page *page, int order,
                                int migratetype)
 {
        spin_lock(&zone->lock);
-       zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);
+       zone->all_unreclaimable = 0;
        zone->pages_scanned = 0;
 
        __mod_zone_page_state(zone, NR_FREE_PAGES, 1 << order);
@@ -583,6 +608,7 @@ static void __free_pages_ok(struct page *page, unsigned int order)
        int bad = 0;
        int wasMlocked = __TestClearPageMlocked(page);
 
+       trace_mm_page_free_direct(page, order);
        kmemcheck_free_shadow(page, order);
 
        for (i = 0 ; i < (1 << order) ; ++i)
@@ -1073,8 +1099,9 @@ void mark_free_pages(struct zone *zone)
 
 /*
  * Free a 0-order page
+ * cold == 1 ? free a cold page : free a hot page
  */
-static void free_hot_cold_page(struct page *page, int cold)
+void free_hot_cold_page(struct page *page, int cold)
 {
        struct zone *zone = page_zone(page);
        struct per_cpu_pages *pcp;
@@ -1082,6 +1109,7 @@ static void free_hot_cold_page(struct page *page, int cold)
        int migratetype;
        int wasMlocked = __TestClearPageMlocked(page);
 
+       trace_mm_page_free_direct(page, 0);
        kmemcheck_free_shadow(page, 0);
 
        if (PageAnon(page))
@@ -1133,12 +1161,6 @@ out:
        local_irq_restore(flags);
 }
 
-void free_hot_page(struct page *page)
-{
-       trace_mm_page_free_direct(page, 0);
-       free_hot_cold_page(page, 0);
-}
-       
 /*
  * split_page takes a non-compound higher-order page, and splits it into
  * n (1<<order) sub-pages: page[0..n]
@@ -2008,9 +2030,8 @@ void __pagevec_free(struct pagevec *pvec)
 void __free_pages(struct page *page, unsigned int order)
 {
        if (put_page_testzero(page)) {
-               trace_mm_page_free_direct(page, order);
                if (order == 0)
-                       free_hot_page(page);
+                       free_hot_cold_page(page, 0);
                else
                        __free_pages_ok(page, order);
        }
@@ -2266,7 +2287,7 @@ void show_free_areas(void)
                        K(zone_page_state(zone, NR_BOUNCE)),
                        K(zone_page_state(zone, NR_WRITEBACK_TEMP)),
                        zone->pages_scanned,
-                       (zone_is_all_unreclaimable(zone) ? "yes" : "no")
+                       (zone->all_unreclaimable ? "yes" : "no")
                        );
                printk("lowmem_reserve[]:");
                for (i = 0; i < MAX_NR_ZONES; i++)
@@ -4371,8 +4392,12 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)
        for (i = 0; i < MAX_NR_ZONES; i++) {
                if (i == ZONE_MOVABLE)
                        continue;
-               printk("  %-8s %0#10lx -> %0#10lx\n",
-                               zone_names[i],
+               printk("  %-8s ", zone_names[i]);
+               if (arch_zone_lowest_possible_pfn[i] ==
+                               arch_zone_highest_possible_pfn[i])
+                       printk("empty\n");
+               else
+                       printk("%0#10lx -> %0#10lx\n",
                                arch_zone_lowest_possible_pfn[i],
                                arch_zone_highest_possible_pfn[i]);
        }
index 033bc135a41f3885cae863ca0c9f5185d6c5d665..337b20e946f6891ed9ae322d22c8e24a1b27f40e 100644 (file)
@@ -501,6 +501,12 @@ void page_cache_sync_readahead(struct address_space *mapping,
        if (!ra->ra_pages)
                return;
 
+       /* be dumb */
+       if (filp->f_mode & FMODE_RANDOM) {
+               force_page_cache_readahead(mapping, filp, offset, req_size);
+               return;
+       }
+
        /* do read-ahead */
        ondemand_readahead(mapping, ra, filp, false, offset, req_size);
 }
index 278cd277bdec75cfb3de7bb01157ad2e092f622a..fcd593c9c997153e78737fc9243d84499205590a 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -62,6 +62,7 @@
 #include "internal.h"
 
 static struct kmem_cache *anon_vma_cachep;
+static struct kmem_cache *anon_vma_chain_cachep;
 
 static inline struct anon_vma *anon_vma_alloc(void)
 {
@@ -73,6 +74,16 @@ void anon_vma_free(struct anon_vma *anon_vma)
        kmem_cache_free(anon_vma_cachep, anon_vma);
 }
 
+static inline struct anon_vma_chain *anon_vma_chain_alloc(void)
+{
+       return kmem_cache_alloc(anon_vma_chain_cachep, GFP_KERNEL);
+}
+
+void anon_vma_chain_free(struct anon_vma_chain *anon_vma_chain)
+{
+       kmem_cache_free(anon_vma_chain_cachep, anon_vma_chain);
+}
+
 /**
  * anon_vma_prepare - attach an anon_vma to a memory region
  * @vma: the memory region in question
@@ -103,18 +114,23 @@ void anon_vma_free(struct anon_vma *anon_vma)
 int anon_vma_prepare(struct vm_area_struct *vma)
 {
        struct anon_vma *anon_vma = vma->anon_vma;
+       struct anon_vma_chain *avc;
 
        might_sleep();
        if (unlikely(!anon_vma)) {
                struct mm_struct *mm = vma->vm_mm;
                struct anon_vma *allocated;
 
+               avc = anon_vma_chain_alloc();
+               if (!avc)
+                       goto out_enomem;
+
                anon_vma = find_mergeable_anon_vma(vma);
                allocated = NULL;
                if (!anon_vma) {
                        anon_vma = anon_vma_alloc();
                        if (unlikely(!anon_vma))
-                               return -ENOMEM;
+                               goto out_enomem_free_avc;
                        allocated = anon_vma;
                }
                spin_lock(&anon_vma->lock);
@@ -123,53 +139,113 @@ int anon_vma_prepare(struct vm_area_struct *vma)
                spin_lock(&mm->page_table_lock);
                if (likely(!vma->anon_vma)) {
                        vma->anon_vma = anon_vma;
-                       list_add_tail(&vma->anon_vma_node, &anon_vma->head);
+                       avc->anon_vma = anon_vma;
+                       avc->vma = vma;
+                       list_add(&avc->same_vma, &vma->anon_vma_chain);
+                       list_add(&avc->same_anon_vma, &anon_vma->head);
                        allocated = NULL;
                }
                spin_unlock(&mm->page_table_lock);
 
                spin_unlock(&anon_vma->lock);
-               if (unlikely(allocated))
+               if (unlikely(allocated)) {
                        anon_vma_free(allocated);
+                       anon_vma_chain_free(avc);
+               }
        }
        return 0;
+
+ out_enomem_free_avc:
+       anon_vma_chain_free(avc);
+ out_enomem:
+       return -ENOMEM;
 }
 
-void __anon_vma_merge(struct vm_area_struct *vma, struct vm_area_struct *next)
+static void anon_vma_chain_link(struct vm_area_struct *vma,
+                               struct anon_vma_chain *avc,
+                               struct anon_vma *anon_vma)
 {
-       BUG_ON(vma->anon_vma != next->anon_vma);
-       list_del(&next->anon_vma_node);
+       avc->vma = vma;
+       avc->anon_vma = anon_vma;
+       list_add(&avc->same_vma, &vma->anon_vma_chain);
+
+       spin_lock(&anon_vma->lock);
+       list_add_tail(&avc->same_anon_vma, &anon_vma->head);
+       spin_unlock(&anon_vma->lock);
 }
 
-void __anon_vma_link(struct vm_area_struct *vma)
+/*
+ * Attach the anon_vmas from src to dst.
+ * Returns 0 on success, -ENOMEM on failure.
+ */
+int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
 {
-       struct anon_vma *anon_vma = vma->anon_vma;
+       struct anon_vma_chain *avc, *pavc;
 
-       if (anon_vma)
-               list_add_tail(&vma->anon_vma_node, &anon_vma->head);
+       list_for_each_entry(pavc, &src->anon_vma_chain, same_vma) {
+               avc = anon_vma_chain_alloc();
+               if (!avc)
+                       goto enomem_failure;
+               anon_vma_chain_link(dst, avc, pavc->anon_vma);
+       }
+       return 0;
+
+ enomem_failure:
+       unlink_anon_vmas(dst);
+       return -ENOMEM;
 }
 
-void anon_vma_link(struct vm_area_struct *vma)
+/*
+ * Attach vma to its own anon_vma, as well as to the anon_vmas that
+ * the corresponding VMA in the parent process is attached to.
+ * Returns 0 on success, non-zero on failure.
+ */
+int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
 {
-       struct anon_vma *anon_vma = vma->anon_vma;
+       struct anon_vma_chain *avc;
+       struct anon_vma *anon_vma;
 
-       if (anon_vma) {
-               spin_lock(&anon_vma->lock);
-               list_add_tail(&vma->anon_vma_node, &anon_vma->head);
-               spin_unlock(&anon_vma->lock);
-       }
+       /* Don't bother if the parent process has no anon_vma here. */
+       if (!pvma->anon_vma)
+               return 0;
+
+       /*
+        * First, attach the new VMA to the parent VMA's anon_vmas,
+        * so rmap can find non-COWed pages in child processes.
+        */
+       if (anon_vma_clone(vma, pvma))
+               return -ENOMEM;
+
+       /* Then add our own anon_vma. */
+       anon_vma = anon_vma_alloc();
+       if (!anon_vma)
+               goto out_error;
+       avc = anon_vma_chain_alloc();
+       if (!avc)
+               goto out_error_free_anon_vma;
+       anon_vma_chain_link(vma, avc, anon_vma);
+       /* Mark this anon_vma as the one where our new (COWed) pages go. */
+       vma->anon_vma = anon_vma;
+
+       return 0;
+
+ out_error_free_anon_vma:
+       anon_vma_free(anon_vma);
+ out_error:
+       return -ENOMEM;
 }
 
-void anon_vma_unlink(struct vm_area_struct *vma)
+static void anon_vma_unlink(struct anon_vma_chain *anon_vma_chain)
 {
-       struct anon_vma *anon_vma = vma->anon_vma;
+       struct anon_vma *anon_vma = anon_vma_chain->anon_vma;
        int empty;
 
+       /* If anon_vma_fork fails, we can get an empty anon_vma_chain. */
        if (!anon_vma)
                return;
 
        spin_lock(&anon_vma->lock);
-       list_del(&vma->anon_vma_node);
+       list_del(&anon_vma_chain->same_anon_vma);
 
        /* We must garbage collect the anon_vma if it's empty */
        empty = list_empty(&anon_vma->head) && !ksm_refcount(anon_vma);
@@ -179,6 +255,18 @@ void anon_vma_unlink(struct vm_area_struct *vma)
                anon_vma_free(anon_vma);
 }
 
+void unlink_anon_vmas(struct vm_area_struct *vma)
+{
+       struct anon_vma_chain *avc, *next;
+
+       /* Unlink each anon_vma chained to the VMA. */
+       list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
+               anon_vma_unlink(avc);
+               list_del(&avc->same_vma);
+               anon_vma_chain_free(avc);
+       }
+}
+
 static void anon_vma_ctor(void *data)
 {
        struct anon_vma *anon_vma = data;
@@ -192,6 +280,7 @@ void __init anon_vma_init(void)
 {
        anon_vma_cachep = kmem_cache_create("anon_vma", sizeof(struct anon_vma),
                        0, SLAB_DESTROY_BY_RCU|SLAB_PANIC, anon_vma_ctor);
+       anon_vma_chain_cachep = KMEM_CACHE(anon_vma_chain, SLAB_PANIC);
 }
 
 /*
@@ -396,7 +485,7 @@ static int page_referenced_anon(struct page *page,
 {
        unsigned int mapcount;
        struct anon_vma *anon_vma;
-       struct vm_area_struct *vma;
+       struct anon_vma_chain *avc;
        int referenced = 0;
 
        anon_vma = page_lock_anon_vma(page);
@@ -404,7 +493,8 @@ static int page_referenced_anon(struct page *page,
                return referenced;
 
        mapcount = page_mapcount(page);
-       list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+       list_for_each_entry(avc, &anon_vma->head, same_anon_vma) {
+               struct vm_area_struct *vma = avc->vma;
                unsigned long address = vma_address(page, vma);
                if (address == -EFAULT)
                        continue;
@@ -511,9 +601,6 @@ int page_referenced(struct page *page,
        int referenced = 0;
        int we_locked = 0;
 
-       if (TestClearPageReferenced(page))
-               referenced++;
-
        *vm_flags = 0;
        if (page_mapped(page) && page_rmapping(page)) {
                if (!is_locked && (!PageAnon(page) || PageKsm(page))) {
@@ -613,6 +700,30 @@ int page_mkclean(struct page *page)
 }
 EXPORT_SYMBOL_GPL(page_mkclean);
 
+/**
+ * page_move_anon_rmap - move a page to our anon_vma
+ * @page:      the page to move to our anon_vma
+ * @vma:       the vma the page belongs to
+ * @address:   the user virtual address mapped
+ *
+ * When a page belongs exclusively to one process after a COW event,
+ * that page can be moved into the anon_vma that belongs to just that
+ * process, so the rmap code will not search the parent or sibling
+ * processes.
+ */
+void page_move_anon_rmap(struct page *page,
+       struct vm_area_struct *vma, unsigned long address)
+{
+       struct anon_vma *anon_vma = vma->anon_vma;
+
+       VM_BUG_ON(!PageLocked(page));
+       VM_BUG_ON(!anon_vma);
+       VM_BUG_ON(page->index != linear_page_index(vma, address));
+
+       anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
+       page->mapping = (struct address_space *) anon_vma;
+}
+
 /**
  * __page_set_anon_rmap - setup new anonymous rmap
  * @page:      the page to add the mapping to
@@ -652,9 +763,6 @@ static void __page_check_anon_rmap(struct page *page,
         * are initially only visible via the pagetables, and the pte is locked
         * over the call to page_add_new_anon_rmap.
         */
-       struct anon_vma *anon_vma = vma->anon_vma;
-       anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
-       BUG_ON(page->mapping != (struct address_space *)anon_vma);
        BUG_ON(page->index != linear_page_index(vma, address));
 #endif
 }
@@ -815,9 +923,9 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
 
        if (PageHWPoison(page) && !(flags & TTU_IGNORE_HWPOISON)) {
                if (PageAnon(page))
-                       dec_mm_counter(mm, anon_rss);
+                       dec_mm_counter(mm, MM_ANONPAGES);
                else
-                       dec_mm_counter(mm, file_rss);
+                       dec_mm_counter(mm, MM_FILEPAGES);
                set_pte_at(mm, address, pte,
                                swp_entry_to_pte(make_hwpoison_entry(page)));
        } else if (PageAnon(page)) {
@@ -839,7 +947,8 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                                        list_add(&mm->mmlist, &init_mm.mmlist);
                                spin_unlock(&mmlist_lock);
                        }
-                       dec_mm_counter(mm, anon_rss);
+                       dec_mm_counter(mm, MM_ANONPAGES);
+                       inc_mm_counter(mm, MM_SWAPENTS);
                } else if (PAGE_MIGRATION) {
                        /*
                         * Store the pfn of the page in a special migration
@@ -857,7 +966,7 @@ int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                entry = make_migration_entry(page, pte_write(pteval));
                set_pte_at(mm, address, pte, swp_entry_to_pte(entry));
        } else
-               dec_mm_counter(mm, file_rss);
+               dec_mm_counter(mm, MM_FILEPAGES);
 
        page_remove_rmap(page);
        page_cache_release(page);
@@ -996,7 +1105,7 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount,
 
                page_remove_rmap(page);
                page_cache_release(page);
-               dec_mm_counter(mm, file_rss);
+               dec_mm_counter(mm, MM_FILEPAGES);
                (*mapcount)--;
        }
        pte_unmap_unlock(pte - 1, ptl);
@@ -1024,14 +1133,15 @@ static int try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount,
 static int try_to_unmap_anon(struct page *page, enum ttu_flags flags)
 {
        struct anon_vma *anon_vma;
-       struct vm_area_struct *vma;
+       struct anon_vma_chain *avc;
        int ret = SWAP_AGAIN;
 
        anon_vma = page_lock_anon_vma(page);
        if (!anon_vma)
                return ret;
 
-       list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+       list_for_each_entry(avc, &anon_vma->head, same_anon_vma) {
+               struct vm_area_struct *vma = avc->vma;
                unsigned long address = vma_address(page, vma);
                if (address == -EFAULT)
                        continue;
@@ -1222,7 +1332,7 @@ static int rmap_walk_anon(struct page *page, int (*rmap_one)(struct page *,
                struct vm_area_struct *, unsigned long, void *), void *arg)
 {
        struct anon_vma *anon_vma;
-       struct vm_area_struct *vma;
+       struct anon_vma_chain *avc;
        int ret = SWAP_AGAIN;
 
        /*
@@ -1237,7 +1347,8 @@ static int rmap_walk_anon(struct page *page, int (*rmap_one)(struct page *,
        if (!anon_vma)
                return ret;
        spin_lock(&anon_vma->lock);
-       list_for_each_entry(vma, &anon_vma->head, anon_vma_node) {
+       list_for_each_entry(avc, &anon_vma->head, same_anon_vma) {
+               struct vm_area_struct *vma = avc->vma;
                unsigned long address = vma_address(page, vma);
                if (address == -EFAULT)
                        continue;
index 7451bdacaf18875044ea97ed3d49da8c401d8249..a9f325b28bed145a4aab6ff8414b074d3b233280 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -935,7 +935,6 @@ static int transfer_objects(struct array_cache *to,
 
        from->avail -= nr;
        to->avail += nr;
-       to->touched = 1;
        return nr;
 }
 
@@ -983,13 +982,11 @@ static struct array_cache **alloc_alien_cache(int node, int limit, gfp_t gfp)
 
        if (limit > 1)
                limit = 12;
-       ac_ptr = kmalloc_node(memsize, gfp, node);
+       ac_ptr = kzalloc_node(memsize, gfp, node);
        if (ac_ptr) {
                for_each_node(i) {
-                       if (i == node || !node_online(i)) {
-                               ac_ptr[i] = NULL;
+                       if (i == node || !node_online(i))
                                continue;
-                       }
                        ac_ptr[i] = alloc_arraycache(node, limit, 0xbaadf00d, gfp);
                        if (!ac_ptr[i]) {
                                for (i--; i >= 0; i--)
@@ -2963,8 +2960,10 @@ retry:
        spin_lock(&l3->list_lock);
 
        /* See if we can refill from the shared array */
-       if (l3->shared && transfer_objects(ac, l3->shared, batchcount))
+       if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) {
+               l3->shared->touched = 1;
                goto alloc_done;
+       }
 
        while (batchcount > 0) {
                struct list_head *entry;
@@ -3101,7 +3100,7 @@ static bool slab_should_failslab(struct kmem_cache *cachep, gfp_t flags)
        if (cachep == &cache_cache)
                return false;
 
-       return should_failslab(obj_size(cachep), flags);
+       return should_failslab(obj_size(cachep), flags, cachep->flags);
 }
 
 static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
index 8d71aaf888d770b27ba26df5d4d9592832a87b2d..0bfd3863d521b29dfd25888bdd3692ef74ea4495 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
  * Set of flags that will prevent slab merging
  */
 #define SLUB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
-               SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE)
+               SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \
+               SLAB_FAILSLAB)
 
 #define SLUB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \
                SLAB_CACHE_DMA | SLAB_NOTRACK)
@@ -217,10 +218,10 @@ static inline void sysfs_slab_remove(struct kmem_cache *s)
 
 #endif
 
-static inline void stat(struct kmem_cache_cpu *c, enum stat_item si)
+static inline void stat(struct kmem_cache *s, enum stat_item si)
 {
 #ifdef CONFIG_SLUB_STATS
-       c->stat[si]++;
+       __this_cpu_inc(s->cpu_slab->stat[si]);
 #endif
 }
 
@@ -242,15 +243,6 @@ static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node)
 #endif
 }
 
-static inline struct kmem_cache_cpu *get_cpu_slab(struct kmem_cache *s, int cpu)
-{
-#ifdef CONFIG_SMP
-       return s->cpu_slab[cpu];
-#else
-       return &s->cpu_slab;
-#endif
-}
-
 /* Verify that a pointer has an address that is valid within a slab page */
 static inline int check_valid_pointer(struct kmem_cache *s,
                                struct page *page, const void *object)
@@ -269,13 +261,6 @@ static inline int check_valid_pointer(struct kmem_cache *s,
        return 1;
 }
 
-/*
- * Slow version of get and set free pointer.
- *
- * This version requires touching the cache lines of kmem_cache which
- * we avoid to do in the fast alloc free paths. There we obtain the offset
- * from the page struct.
- */
 static inline void *get_freepointer(struct kmem_cache *s, void *object)
 {
        return *(void **)(object + s->offset);
@@ -1020,6 +1005,9 @@ static int __init setup_slub_debug(char *str)
                case 't':
                        slub_debug |= SLAB_TRACE;
                        break;
+               case 'a':
+                       slub_debug |= SLAB_FAILSLAB;
+                       break;
                default:
                        printk(KERN_ERR "slub_debug option '%c' "
                                "unknown. skipped\n", *str);
@@ -1124,7 +1112,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
                if (!page)
                        return NULL;
 
-               stat(get_cpu_slab(s, raw_smp_processor_id()), ORDER_FALLBACK);
+               stat(s, ORDER_FALLBACK);
        }
 
        if (kmemcheck_enabled
@@ -1422,23 +1410,22 @@ static struct page *get_partial(struct kmem_cache *s, gfp_t flags, int node)
 static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail)
 {
        struct kmem_cache_node *n = get_node(s, page_to_nid(page));
-       struct kmem_cache_cpu *c = get_cpu_slab(s, smp_processor_id());
 
        __ClearPageSlubFrozen(page);
        if (page->inuse) {
 
                if (page->freelist) {
                        add_partial(n, page, tail);
-                       stat(c, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD);
+                       stat(s, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD);
                } else {
-                       stat(c, DEACTIVATE_FULL);
+                       stat(s, DEACTIVATE_FULL);
                        if (SLABDEBUG && PageSlubDebug(page) &&
                                                (s->flags & SLAB_STORE_USER))
                                add_full(n, page);
                }
                slab_unlock(page);
        } else {
-               stat(c, DEACTIVATE_EMPTY);
+               stat(s, DEACTIVATE_EMPTY);
                if (n->nr_partial < s->min_partial) {
                        /*
                         * Adding an empty slab to the partial slabs in order
@@ -1454,7 +1441,7 @@ static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail)
                        slab_unlock(page);
                } else {
                        slab_unlock(page);
-                       stat(get_cpu_slab(s, raw_smp_processor_id()), FREE_SLAB);
+                       stat(s, FREE_SLAB);
                        discard_slab(s, page);
                }
        }
@@ -1469,7 +1456,7 @@ static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
        int tail = 1;
 
        if (page->freelist)
-               stat(c, DEACTIVATE_REMOTE_FREES);
+               stat(s, DEACTIVATE_REMOTE_FREES);
        /*
         * Merge cpu freelist into slab freelist. Typically we get here
         * because both freelists are empty. So this is unlikely
@@ -1482,10 +1469,10 @@ static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
 
                /* Retrieve object from cpu_freelist */
                object = c->freelist;
-               c->freelist = c->freelist[c->offset];
+               c->freelist = get_freepointer(s, c->freelist);
 
                /* And put onto the regular freelist */
-               object[c->offset] = page->freelist;
+               set_freepointer(s, object, page->freelist);
                page->freelist = object;
                page->inuse--;
        }
@@ -1495,7 +1482,7 @@ static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
 
 static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
 {
-       stat(c, CPUSLAB_FLUSH);
+       stat(s, CPUSLAB_FLUSH);
        slab_lock(c->page);
        deactivate_slab(s, c);
 }
@@ -1507,7 +1494,7 @@ static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
  */
 static inline void __flush_cpu_slab(struct kmem_cache *s, int cpu)
 {
-       struct kmem_cache_cpu *c = get_cpu_slab(s, cpu);
+       struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu);
 
        if (likely(c && c->page))
                flush_slab(s, c);
@@ -1635,7 +1622,7 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
        if (unlikely(!node_match(c, node)))
                goto another_slab;
 
-       stat(c, ALLOC_REFILL);
+       stat(s, ALLOC_REFILL);
 
 load_freelist:
        object = c->page->freelist;
@@ -1644,13 +1631,13 @@ load_freelist:
        if (unlikely(SLABDEBUG && PageSlubDebug(c->page)))
                goto debug;
 
-       c->freelist = object[c->offset];
+       c->freelist = get_freepointer(s, object);
        c->page->inuse = c->page->objects;
        c->page->freelist = NULL;
        c->node = page_to_nid(c->page);
 unlock_out:
        slab_unlock(c->page);
-       stat(c, ALLOC_SLOWPATH);
+       stat(s, ALLOC_SLOWPATH);
        return object;
 
 another_slab:
@@ -1660,7 +1647,7 @@ new_slab:
        new = get_partial(s, gfpflags, node);
        if (new) {
                c->page = new;
-               stat(c, ALLOC_FROM_PARTIAL);
+               stat(s, ALLOC_FROM_PARTIAL);
                goto load_freelist;
        }
 
@@ -1673,8 +1660,8 @@ new_slab:
                local_irq_disable();
 
        if (new) {
-               c = get_cpu_slab(s, smp_processor_id());
-               stat(c, ALLOC_SLAB);
+               c = __this_cpu_ptr(s->cpu_slab);
+               stat(s, ALLOC_SLAB);
                if (c->page)
                        flush_slab(s, c);
                slab_lock(new);
@@ -1690,7 +1677,7 @@ debug:
                goto another_slab;
 
        c->page->inuse++;
-       c->page->freelist = object[c->offset];
+       c->page->freelist = get_freepointer(s, object);
        c->node = -1;
        goto unlock_out;
 }
@@ -1711,35 +1698,33 @@ static __always_inline void *slab_alloc(struct kmem_cache *s,
        void **object;
        struct kmem_cache_cpu *c;
        unsigned long flags;
-       unsigned int objsize;
 
        gfpflags &= gfp_allowed_mask;
 
        lockdep_trace_alloc(gfpflags);
        might_sleep_if(gfpflags & __GFP_WAIT);
 
-       if (should_failslab(s->objsize, gfpflags))
+       if (should_failslab(s->objsize, gfpflags, s->flags))
                return NULL;
 
        local_irq_save(flags);
-       c = get_cpu_slab(s, smp_processor_id());
-       objsize = c->objsize;
-       if (unlikely(!c->freelist || !node_match(c, node)))
+       c = __this_cpu_ptr(s->cpu_slab);
+       object = c->freelist;
+       if (unlikely(!object || !node_match(c, node)))
 
                object = __slab_alloc(s, gfpflags, node, addr, c);
 
        else {
-               object = c->freelist;
-               c->freelist = object[c->offset];
-               stat(c, ALLOC_FASTPATH);
+               c->freelist = get_freepointer(s, object);
+               stat(s, ALLOC_FASTPATH);
        }
        local_irq_restore(flags);
 
        if (unlikely(gfpflags & __GFP_ZERO) && object)
-               memset(object, 0, objsize);
+               memset(object, 0, s->objsize);
 
-       kmemcheck_slab_alloc(s, gfpflags, object, c->objsize);
-       kmemleak_alloc_recursive(object, objsize, 1, s->flags, gfpflags);
+       kmemcheck_slab_alloc(s, gfpflags, object, s->objsize);
+       kmemleak_alloc_recursive(object, s->objsize, 1, s->flags, gfpflags);
 
        return object;
 }
@@ -1794,26 +1779,25 @@ EXPORT_SYMBOL(kmem_cache_alloc_node_notrace);
  * handling required then we can return immediately.
  */
 static void __slab_free(struct kmem_cache *s, struct page *page,
-                       void *x, unsigned long addr, unsigned int offset)
+                       void *x, unsigned long addr)
 {
        void *prior;
        void **object = (void *)x;
-       struct kmem_cache_cpu *c;
 
-       c = get_cpu_slab(s, raw_smp_processor_id());
-       stat(c, FREE_SLOWPATH);
+       stat(s, FREE_SLOWPATH);
        slab_lock(page);
 
        if (unlikely(SLABDEBUG && PageSlubDebug(page)))
                goto debug;
 
 checks_ok:
-       prior = object[offset] = page->freelist;
+       prior = page->freelist;
+       set_freepointer(s, object, prior);
        page->freelist = object;
        page->inuse--;
 
        if (unlikely(PageSlubFrozen(page))) {
-               stat(c, FREE_FROZEN);
+               stat(s, FREE_FROZEN);
                goto out_unlock;
        }
 
@@ -1826,7 +1810,7 @@ checks_ok:
         */
        if (unlikely(!prior)) {
                add_partial(get_node(s, page_to_nid(page)), page, 1);
-               stat(c, FREE_ADD_PARTIAL);
+               stat(s, FREE_ADD_PARTIAL);
        }
 
 out_unlock:
@@ -1839,10 +1823,10 @@ slab_empty:
                 * Slab still on the partial list.
                 */
                remove_partial(s, page);
-               stat(c, FREE_REMOVE_PARTIAL);
+               stat(s, FREE_REMOVE_PARTIAL);
        }
        slab_unlock(page);
-       stat(c, FREE_SLAB);
+       stat(s, FREE_SLAB);
        discard_slab(s, page);
        return;
 
@@ -1872,17 +1856,17 @@ static __always_inline void slab_free(struct kmem_cache *s,
 
        kmemleak_free_recursive(x, s->flags);
        local_irq_save(flags);
-       c = get_cpu_slab(s, smp_processor_id());
-       kmemcheck_slab_free(s, object, c->objsize);
-       debug_check_no_locks_freed(object, c->objsize);
+       c = __this_cpu_ptr(s->cpu_slab);
+       kmemcheck_slab_free(s, object, s->objsize);
+       debug_check_no_locks_freed(object, s->objsize);
        if (!(s->flags & SLAB_DEBUG_OBJECTS))
-               debug_check_no_obj_freed(object, c->objsize);
+               debug_check_no_obj_freed(object, s->objsize);
        if (likely(page == c->page && c->node >= 0)) {
-               object[c->offset] = c->freelist;
+               set_freepointer(s, object, c->freelist);
                c->freelist = object;
-               stat(c, FREE_FASTPATH);
+               stat(s, FREE_FASTPATH);
        } else
-               __slab_free(s, page, x, addr, c->offset);
+               __slab_free(s, page, x, addr);
 
        local_irq_restore(flags);
 }
@@ -2069,19 +2053,6 @@ static unsigned long calculate_alignment(unsigned long flags,
        return ALIGN(align, sizeof(void *));
 }
 
-static void init_kmem_cache_cpu(struct kmem_cache *s,
-                       struct kmem_cache_cpu *c)
-{
-       c->page = NULL;
-       c->freelist = NULL;
-       c->node = 0;
-       c->offset = s->offset / sizeof(void *);
-       c->objsize = s->objsize;
-#ifdef CONFIG_SLUB_STATS
-       memset(c->stat, 0, NR_SLUB_STAT_ITEMS * sizeof(unsigned));
-#endif
-}
-
 static void
 init_kmem_cache_node(struct kmem_cache_node *n, struct kmem_cache *s)
 {
@@ -2095,130 +2066,24 @@ init_kmem_cache_node(struct kmem_cache_node *n, struct kmem_cache *s)
 #endif
 }
 
-#ifdef CONFIG_SMP
-/*
- * Per cpu array for per cpu structures.
- *
- * The per cpu array places all kmem_cache_cpu structures from one processor
- * close together meaning that it becomes possible that multiple per cpu
- * structures are contained in one cacheline. This may be particularly
- * beneficial for the kmalloc caches.
- *
- * A desktop system typically has around 60-80 slabs. With 100 here we are
- * likely able to get per cpu structures for all caches from the array defined
- * here. We must be able to cover all kmalloc caches during bootstrap.
- *
- * If the per cpu array is exhausted then fall back to kmalloc
- * of individual cachelines. No sharing is possible then.
- */
-#define NR_KMEM_CACHE_CPU 100
-
-static DEFINE_PER_CPU(struct kmem_cache_cpu [NR_KMEM_CACHE_CPU],
-                     kmem_cache_cpu);
-
-static DEFINE_PER_CPU(struct kmem_cache_cpu *, kmem_cache_cpu_free);
-static DECLARE_BITMAP(kmem_cach_cpu_free_init_once, CONFIG_NR_CPUS);
-
-static struct kmem_cache_cpu *alloc_kmem_cache_cpu(struct kmem_cache *s,
-                                                       int cpu, gfp_t flags)
-{
-       struct kmem_cache_cpu *c = per_cpu(kmem_cache_cpu_free, cpu);
-
-       if (c)
-               per_cpu(kmem_cache_cpu_free, cpu) =
-                               (void *)c->freelist;
-       else {
-               /* Table overflow: So allocate ourselves */
-               c = kmalloc_node(
-                       ALIGN(sizeof(struct kmem_cache_cpu), cache_line_size()),
-                       flags, cpu_to_node(cpu));
-               if (!c)
-                       return NULL;
-       }
-
-       init_kmem_cache_cpu(s, c);
-       return c;
-}
-
-static void free_kmem_cache_cpu(struct kmem_cache_cpu *c, int cpu)
-{
-       if (c < per_cpu(kmem_cache_cpu, cpu) ||
-                       c >= per_cpu(kmem_cache_cpu, cpu) + NR_KMEM_CACHE_CPU) {
-               kfree(c);
-               return;
-       }
-       c->freelist = (void *)per_cpu(kmem_cache_cpu_free, cpu);
-       per_cpu(kmem_cache_cpu_free, cpu) = c;
-}
-
-static void free_kmem_cache_cpus(struct kmem_cache *s)
-{
-       int cpu;
-
-       for_each_online_cpu(cpu) {
-               struct kmem_cache_cpu *c = get_cpu_slab(s, cpu);
-
-               if (c) {
-                       s->cpu_slab[cpu] = NULL;
-                       free_kmem_cache_cpu(c, cpu);
-               }
-       }
-}
-
-static int alloc_kmem_cache_cpus(struct kmem_cache *s, gfp_t flags)
-{
-       int cpu;
-
-       for_each_online_cpu(cpu) {
-               struct kmem_cache_cpu *c = get_cpu_slab(s, cpu);
-
-               if (c)
-                       continue;
-
-               c = alloc_kmem_cache_cpu(s, cpu, flags);
-               if (!c) {
-                       free_kmem_cache_cpus(s);
-                       return 0;
-               }
-               s->cpu_slab[cpu] = c;
-       }
-       return 1;
-}
-
-/*
- * Initialize the per cpu array.
- */
-static void init_alloc_cpu_cpu(int cpu)
-{
-       int i;
+static DEFINE_PER_CPU(struct kmem_cache_cpu, kmalloc_percpu[KMALLOC_CACHES]);
 
-       if (cpumask_test_cpu(cpu, to_cpumask(kmem_cach_cpu_free_init_once)))
-               return;
-
-       for (i = NR_KMEM_CACHE_CPU - 1; i >= 0; i--)
-               free_kmem_cache_cpu(&per_cpu(kmem_cache_cpu, cpu)[i], cpu);
-
-       cpumask_set_cpu(cpu, to_cpumask(kmem_cach_cpu_free_init_once));
-}
-
-static void __init init_alloc_cpu(void)
+static inline int alloc_kmem_cache_cpus(struct kmem_cache *s, gfp_t flags)
 {
-       int cpu;
-
-       for_each_online_cpu(cpu)
-               init_alloc_cpu_cpu(cpu);
-  }
+       if (s < kmalloc_caches + KMALLOC_CACHES && s >= kmalloc_caches)
+               /*
+                * Boot time creation of the kmalloc array. Use static per cpu data
+                * since the per cpu allocator is not available yet.
+                */
+               s->cpu_slab = kmalloc_percpu + (s - kmalloc_caches);
+       else
+               s->cpu_slab =  alloc_percpu(struct kmem_cache_cpu);
 
-#else
-static inline void free_kmem_cache_cpus(struct kmem_cache *s) {}
-static inline void init_alloc_cpu(void) {}
+       if (!s->cpu_slab)
+               return 0;
 
-static inline int alloc_kmem_cache_cpus(struct kmem_cache *s, gfp_t flags)
-{
-       init_kmem_cache_cpu(s, &s->cpu_slab);
        return 1;
 }
-#endif
 
 #ifdef CONFIG_NUMA
 /*
@@ -2287,7 +2152,8 @@ static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t gfpflags)
        int node;
        int local_node;
 
-       if (slab_state >= UP)
+       if (slab_state >= UP && (s < kmalloc_caches ||
+                       s > kmalloc_caches + KMALLOC_CACHES))
                local_node = page_to_nid(virt_to_page(s));
        else
                local_node = 0;
@@ -2502,6 +2368,7 @@ static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags,
 
        if (alloc_kmem_cache_cpus(s, gfpflags & ~SLUB_DMA))
                return 1;
+
        free_kmem_cache_nodes(s);
 error:
        if (flags & SLAB_PANIC)
@@ -2609,9 +2476,8 @@ static inline int kmem_cache_close(struct kmem_cache *s)
        int node;
 
        flush_all(s);
-
+       free_percpu(s->cpu_slab);
        /* Attempt to free all objects */
-       free_kmem_cache_cpus(s);
        for_each_node_state(node, N_NORMAL_MEMORY) {
                struct kmem_cache_node *n = get_node(s, node);
 
@@ -2651,7 +2517,7 @@ EXPORT_SYMBOL(kmem_cache_destroy);
  *             Kmalloc subsystem
  *******************************************************************/
 
-struct kmem_cache kmalloc_caches[SLUB_PAGE_SHIFT] __cacheline_aligned;
+struct kmem_cache kmalloc_caches[KMALLOC_CACHES] __cacheline_aligned;
 EXPORT_SYMBOL(kmalloc_caches);
 
 static int __init setup_slub_min_order(char *str)
@@ -2741,6 +2607,7 @@ static noinline struct kmem_cache *dma_kmalloc_cache(int index, gfp_t flags)
        char *text;
        size_t realsize;
        unsigned long slabflags;
+       int i;
 
        s = kmalloc_caches_dma[index];
        if (s)
@@ -2760,7 +2627,14 @@ static noinline struct kmem_cache *dma_kmalloc_cache(int index, gfp_t flags)
        realsize = kmalloc_caches[index].objsize;
        text = kasprintf(flags & ~SLUB_DMA, "kmalloc_dma-%d",
                         (unsigned int)realsize);
-       s = kmalloc(kmem_size, flags & ~SLUB_DMA);
+
+       s = NULL;
+       for (i = 0; i < KMALLOC_CACHES; i++)
+               if (!kmalloc_caches[i].size)
+                       break;
+
+       BUG_ON(i >= KMALLOC_CACHES);
+       s = kmalloc_caches + i;
 
        /*
         * Must defer sysfs creation to a workqueue because we don't know
@@ -2772,9 +2646,9 @@ static noinline struct kmem_cache *dma_kmalloc_cache(int index, gfp_t flags)
        if (slab_state >= SYSFS)
                slabflags |= __SYSFS_ADD_DEFERRED;
 
-       if (!s || !text || !kmem_cache_open(s, flags, text,
+       if (!text || !kmem_cache_open(s, flags, text,
                        realsize, ARCH_KMALLOC_MINALIGN, slabflags, NULL)) {
-               kfree(s);
+               s->size = 0;
                kfree(text);
                goto unlock_out;
        }
@@ -3176,8 +3050,6 @@ void __init kmem_cache_init(void)
        int i;
        int caches = 0;
 
-       init_alloc_cpu();
-
 #ifdef CONFIG_NUMA
        /*
         * Must first have the slab cache available for the allocations of the
@@ -3261,8 +3133,10 @@ void __init kmem_cache_init(void)
 
 #ifdef CONFIG_SMP
        register_cpu_notifier(&slab_notifier);
-       kmem_size = offsetof(struct kmem_cache, cpu_slab) +
-                               nr_cpu_ids * sizeof(struct kmem_cache_cpu *);
+#endif
+#ifdef CONFIG_NUMA
+       kmem_size = offsetof(struct kmem_cache, node) +
+                               nr_node_ids * sizeof(struct kmem_cache_node *);
 #else
        kmem_size = sizeof(struct kmem_cache);
 #endif
@@ -3351,22 +3225,12 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size,
        down_write(&slub_lock);
        s = find_mergeable(size, align, flags, name, ctor);
        if (s) {
-               int cpu;
-
                s->refcount++;
                /*
                 * Adjust the object sizes so that we clear
                 * the complete object on kzalloc.
                 */
                s->objsize = max(s->objsize, (int)size);
-
-               /*
-                * And then we need to update the object size in the
-                * per cpu structures
-                */
-               for_each_online_cpu(cpu)
-                       get_cpu_slab(s, cpu)->objsize = s->objsize;
-
                s->inuse = max_t(int, s->inuse, ALIGN(size, sizeof(void *)));
                up_write(&slub_lock);
 
@@ -3420,29 +3284,15 @@ static int __cpuinit slab_cpuup_callback(struct notifier_block *nfb,
        unsigned long flags;
 
        switch (action) {
-       case CPU_UP_PREPARE:
-       case CPU_UP_PREPARE_FROZEN:
-               init_alloc_cpu_cpu(cpu);
-               down_read(&slub_lock);
-               list_for_each_entry(s, &slab_caches, list)
-                       s->cpu_slab[cpu] = alloc_kmem_cache_cpu(s, cpu,
-                                                       GFP_KERNEL);
-               up_read(&slub_lock);
-               break;
-
        case CPU_UP_CANCELED:
        case CPU_UP_CANCELED_FROZEN:
        case CPU_DEAD:
        case CPU_DEAD_FROZEN:
                down_read(&slub_lock);
                list_for_each_entry(s, &slab_caches, list) {
-                       struct kmem_cache_cpu *c = get_cpu_slab(s, cpu);
-
                        local_irq_save(flags);
                        __flush_cpu_slab(s, cpu);
                        local_irq_restore(flags);
-                       free_kmem_cache_cpu(c, cpu);
-                       s->cpu_slab[cpu] = NULL;
                }
                up_read(&slub_lock);
                break;
@@ -3928,7 +3778,7 @@ static ssize_t show_slab_objects(struct kmem_cache *s,
                int cpu;
 
                for_each_possible_cpu(cpu) {
-                       struct kmem_cache_cpu *c = get_cpu_slab(s, cpu);
+                       struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu);
 
                        if (!c || c->node < 0)
                                continue;
@@ -4171,6 +4021,23 @@ static ssize_t trace_store(struct kmem_cache *s, const char *buf,
 }
 SLAB_ATTR(trace);
 
+#ifdef CONFIG_FAILSLAB
+static ssize_t failslab_show(struct kmem_cache *s, char *buf)
+{
+       return sprintf(buf, "%d\n", !!(s->flags & SLAB_FAILSLAB));
+}
+
+static ssize_t failslab_store(struct kmem_cache *s, const char *buf,
+                                                       size_t length)
+{
+       s->flags &= ~SLAB_FAILSLAB;
+       if (buf[0] == '1')
+               s->flags |= SLAB_FAILSLAB;
+       return length;
+}
+SLAB_ATTR(failslab);
+#endif
+
 static ssize_t reclaim_account_show(struct kmem_cache *s, char *buf)
 {
        return sprintf(buf, "%d\n", !!(s->flags & SLAB_RECLAIM_ACCOUNT));
@@ -4353,7 +4220,7 @@ static int show_stat(struct kmem_cache *s, char *buf, enum stat_item si)
                return -ENOMEM;
 
        for_each_online_cpu(cpu) {
-               unsigned x = get_cpu_slab(s, cpu)->stat[si];
+               unsigned x = per_cpu_ptr(s->cpu_slab, cpu)->stat[si];
 
                data[cpu] = x;
                sum += x;
@@ -4376,7 +4243,7 @@ static void clear_stat(struct kmem_cache *s, enum stat_item si)
        int cpu;
 
        for_each_online_cpu(cpu)
-               get_cpu_slab(s, cpu)->stat[si] = 0;
+               per_cpu_ptr(s->cpu_slab, cpu)->stat[si] = 0;
 }
 
 #define STAT_ATTR(si, text)                                    \
@@ -4467,6 +4334,10 @@ static struct attribute *slab_attrs[] = {
        &deactivate_remote_frees_attr.attr,
        &order_fallback_attr.attr,
 #endif
+#ifdef CONFIG_FAILSLAB
+       &failslab_attr.attr,
+#endif
+
        NULL
 };
 
index 308e57d8d7ed9e256eb26be6b5478da06aac5d7f..9036b89813aca85fabc8df912760747d14b40ad5 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -55,7 +55,7 @@ static void __page_cache_release(struct page *page)
                del_page_from_lru(zone, page);
                spin_unlock_irqrestore(&zone->lru_lock, flags);
        }
-       free_hot_page(page);
+       free_hot_cold_page(page, 0);
 }
 
 static void put_compound_page(struct page *page)
index 6c0585b16418661529ef1c0399450bf4b9128a45..84374d8cf814db6e131be7c42fdbd7450b7282d5 100644 (file)
@@ -840,7 +840,8 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
                goto out;
        }
 
-       inc_mm_counter(vma->vm_mm, anon_rss);
+       dec_mm_counter(vma->vm_mm, MM_SWAPENTS);
+       inc_mm_counter(vma->vm_mm, MM_ANONPAGES);
        get_page(page);
        set_pte_at(vma->vm_mm, addr, pte,
                   pte_mkold(mk_pte(page, vma->vm_page_prot)));
@@ -1759,11 +1760,11 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
        unsigned int type;
        int i, prev;
        int error;
-       union swap_header *swap_header = NULL;
-       unsigned int nr_good_pages = 0;
+       union swap_header *swap_header;
+       unsigned int nr_good_pages;
        int nr_extents = 0;
        sector_t span;
-       unsigned long maxpages = 1;
+       unsigned long maxpages;
        unsigned long swapfilepages;
        unsigned char *swap_map = NULL;
        struct page *page = NULL;
@@ -1922,9 +1923,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
         * swap pte.
         */
        maxpages = swp_offset(pte_to_swp_entry(
-                       swp_entry_to_pte(swp_entry(0, ~0UL)))) - 1;
-       if (maxpages > swap_header->info.last_page)
-               maxpages = swap_header->info.last_page;
+                       swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1;
+       if (maxpages > swap_header->info.last_page) {
+               maxpages = swap_header->info.last_page + 1;
+               /* p->max is an unsigned int: don't overflow it */
+               if ((unsigned int)maxpages == 0)
+                       maxpages = UINT_MAX;
+       }
        p->highest_bit = maxpages - 1;
 
        error = -EINVAL;
@@ -1948,23 +1953,24 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
        }
 
        memset(swap_map, 0, maxpages);
+       nr_good_pages = maxpages - 1;   /* omit header page */
+
        for (i = 0; i < swap_header->info.nr_badpages; i++) {
-               int page_nr = swap_header->info.badpages[i];
-               if (page_nr <= 0 || page_nr >= swap_header->info.last_page) {
+               unsigned int page_nr = swap_header->info.badpages[i];
+               if (page_nr == 0 || page_nr > swap_header->info.last_page) {
                        error = -EINVAL;
                        goto bad_swap;
                }
-               swap_map[page_nr] = SWAP_MAP_BAD;
+               if (page_nr < maxpages) {
+                       swap_map[page_nr] = SWAP_MAP_BAD;
+                       nr_good_pages--;
+               }
        }
 
        error = swap_cgroup_swapon(type, maxpages);
        if (error)
                goto bad_swap;
 
-       nr_good_pages = swap_header->info.last_page -
-                       swap_header->info.nr_badpages -
-                       1 /* header page */;
-
        if (nr_good_pages) {
                swap_map[0] = SWAP_MAP_BAD;
                p->max = maxpages;
@@ -2155,7 +2161,11 @@ void swap_shmem_alloc(swp_entry_t entry)
 }
 
 /*
- * increase reference count of swap entry by 1.
+ * Increase reference count of swap entry by 1.
+ * Returns 0 for success, or -ENOMEM if a swap_count_continuation is required
+ * but could not be atomically allocated.  Returns 0, just as if it succeeded,
+ * if __swap_duplicate() fails for another reason (-EINVAL or -ENOENT), which
+ * might occur if a page table entry has got corrupted.
  */
 int swap_duplicate(swp_entry_t entry)
 {
index c26986c85ce01b1d15a8914065a829890054c210..79c809895fba777d6069ae3778046def74a29b81 100644 (file)
@@ -262,27 +262,6 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
        return ret;
 }
 
-/* Called without lock on whether page is mapped, so answer is unstable */
-static inline int page_mapping_inuse(struct page *page)
-{
-       struct address_space *mapping;
-
-       /* Page is in somebody's page tables. */
-       if (page_mapped(page))
-               return 1;
-
-       /* Be more reluctant to reclaim swapcache than pagecache */
-       if (PageSwapCache(page))
-               return 1;
-
-       mapping = page_mapping(page);
-       if (!mapping)
-               return 0;
-
-       /* File is mmap'd by somebody? */
-       return mapping_mapped(mapping);
-}
-
 static inline int is_page_cache_freeable(struct page *page)
 {
        /*
@@ -579,6 +558,65 @@ redo:
        put_page(page);         /* drop ref from isolate */
 }
 
+enum page_references {
+       PAGEREF_RECLAIM,
+       PAGEREF_RECLAIM_CLEAN,
+       PAGEREF_KEEP,
+       PAGEREF_ACTIVATE,
+};
+
+static enum page_references page_check_references(struct page *page,
+                                                 struct scan_control *sc)
+{
+       int referenced_ptes, referenced_page;
+       unsigned long vm_flags;
+
+       referenced_ptes = page_referenced(page, 1, sc->mem_cgroup, &vm_flags);
+       referenced_page = TestClearPageReferenced(page);
+
+       /* Lumpy reclaim - ignore references */
+       if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
+               return PAGEREF_RECLAIM;
+
+       /*
+        * Mlock lost the isolation race with us.  Let try_to_unmap()
+        * move the page to the unevictable list.
+        */
+       if (vm_flags & VM_LOCKED)
+               return PAGEREF_RECLAIM;
+
+       if (referenced_ptes) {
+               if (PageAnon(page))
+                       return PAGEREF_ACTIVATE;
+               /*
+                * All mapped pages start out with page table
+                * references from the instantiating fault, so we need
+                * to look twice if a mapped file page is used more
+                * than once.
+                *
+                * Mark it and spare it for another trip around the
+                * inactive list.  Another page table reference will
+                * lead to its activation.
+                *
+                * Note: the mark is set for activated pages as well
+                * so that recently deactivated but used pages are
+                * quickly recovered.
+                */
+               SetPageReferenced(page);
+
+               if (referenced_page)
+                       return PAGEREF_ACTIVATE;
+
+               return PAGEREF_KEEP;
+       }
+
+       /* Reclaim if clean, defer dirty pages to writeback */
+       if (referenced_page)
+               return PAGEREF_RECLAIM_CLEAN;
+
+       return PAGEREF_RECLAIM;
+}
+
 /*
  * shrink_page_list() returns the number of reclaimed pages
  */
@@ -590,16 +628,15 @@ static unsigned long shrink_page_list(struct list_head *page_list,
        struct pagevec freed_pvec;
        int pgactivate = 0;
        unsigned long nr_reclaimed = 0;
-       unsigned long vm_flags;
 
        cond_resched();
 
        pagevec_init(&freed_pvec, 1);
        while (!list_empty(page_list)) {
+               enum page_references references;
                struct address_space *mapping;
                struct page *page;
                int may_enter_fs;
-               int referenced;
 
                cond_resched();
 
@@ -641,17 +678,16 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                                goto keep_locked;
                }
 
-               referenced = page_referenced(page, 1,
-                                               sc->mem_cgroup, &vm_flags);
-               /*
-                * In active use or really unfreeable?  Activate it.
-                * If page which have PG_mlocked lost isoltation race,
-                * try_to_unmap moves it to unevictable list
-                */
-               if (sc->order <= PAGE_ALLOC_COSTLY_ORDER &&
-                                       referenced && page_mapping_inuse(page)
-                                       && !(vm_flags & VM_LOCKED))
+               references = page_check_references(page, sc);
+               switch (references) {
+               case PAGEREF_ACTIVATE:
                        goto activate_locked;
+               case PAGEREF_KEEP:
+                       goto keep_locked;
+               case PAGEREF_RECLAIM:
+               case PAGEREF_RECLAIM_CLEAN:
+                       ; /* try to reclaim the page below */
+               }
 
                /*
                 * Anonymous process memory has backing store?
@@ -685,7 +721,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
                }
 
                if (PageDirty(page)) {
-                       if (sc->order <= PAGE_ALLOC_COSTLY_ORDER && referenced)
+                       if (references == PAGEREF_RECLAIM_CLEAN)
                                goto keep_locked;
                        if (!may_enter_fs)
                                goto keep_locked;
@@ -1350,9 +1386,7 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone,
                        continue;
                }
 
-               /* page_referenced clears PageReferenced */
-               if (page_mapping_inuse(page) &&
-                   page_referenced(page, 0, sc->mem_cgroup, &vm_flags)) {
+               if (page_referenced(page, 0, sc->mem_cgroup, &vm_flags)) {
                        nr_rotated++;
                        /*
                         * Identify referenced, file-backed active pages and
@@ -1501,6 +1535,13 @@ static void get_scan_ratio(struct zone *zone, struct scan_control *sc,
        unsigned long ap, fp;
        struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(zone, sc);
 
+       /* If we have no swap space, do not bother scanning anon pages. */
+       if (!sc->may_swap || (nr_swap_pages <= 0)) {
+               percent[0] = 0;
+               percent[1] = 100;
+               return;
+       }
+
        anon  = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_ANON) +
                zone_nr_lru_pages(zone, sc, LRU_INACTIVE_ANON);
        file  = zone_nr_lru_pages(zone, sc, LRU_ACTIVE_FILE) +
@@ -1598,22 +1639,20 @@ static void shrink_zone(int priority, struct zone *zone,
        unsigned long nr_reclaimed = sc->nr_reclaimed;
        unsigned long nr_to_reclaim = sc->nr_to_reclaim;
        struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(zone, sc);
-       int noswap = 0;
 
-       /* If we have no swap space, do not bother scanning anon pages. */
-       if (!sc->may_swap || (nr_swap_pages <= 0)) {
-               noswap = 1;
-               percent[0] = 0;
-               percent[1] = 100;
-       } else
-               get_scan_ratio(zone, sc, percent);
+       get_scan_ratio(zone, sc, percent);
 
        for_each_evictable_lru(l) {
                int file = is_file_lru(l);
                unsigned long scan;
 
+               if (percent[file] == 0) {
+                       nr[l] = 0;
+                       continue;
+               }
+
                scan = zone_nr_lru_pages(zone, sc, l);
-               if (priority || noswap) {
+               if (priority) {
                        scan >>= priority;
                        scan = (scan * percent[file]) / 100;
                }
@@ -1694,8 +1733,7 @@ static void shrink_zones(int priority, struct zonelist *zonelist,
                                continue;
                        note_zone_scanning_priority(zone, priority);
 
-                       if (zone_is_all_unreclaimable(zone) &&
-                                               priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
                                continue;       /* Let kswapd poll it */
                        sc->all_unreclaimable = 0;
                } else {
@@ -1922,7 +1960,7 @@ static int sleeping_prematurely(pg_data_t *pgdat, int order, long remaining)
                if (!populated_zone(zone))
                        continue;
 
-               if (zone_is_all_unreclaimable(zone))
+               if (zone->all_unreclaimable)
                        continue;
 
                if (!zone_watermark_ok(zone, order, high_wmark_pages(zone),
@@ -2012,8 +2050,7 @@ loop_again:
                        if (!populated_zone(zone))
                                continue;
 
-                       if (zone_is_all_unreclaimable(zone) &&
-                           priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
                                continue;
 
                        /*
@@ -2056,13 +2093,9 @@ loop_again:
                        if (!populated_zone(zone))
                                continue;
 
-                       if (zone_is_all_unreclaimable(zone) &&
-                                       priority != DEF_PRIORITY)
+                       if (zone->all_unreclaimable && priority != DEF_PRIORITY)
                                continue;
 
-                       if (!zone_watermark_ok(zone, order,
-                                       high_wmark_pages(zone), end_zone, 0))
-                               all_zones_ok = 0;
                        temp_priority[i] = priority;
                        sc.nr_scanned = 0;
                        note_zone_scanning_priority(zone, priority);
@@ -2087,12 +2120,11 @@ loop_again:
                                                lru_pages);
                        sc.nr_reclaimed += reclaim_state->reclaimed_slab;
                        total_scanned += sc.nr_scanned;
-                       if (zone_is_all_unreclaimable(zone))
+                       if (zone->all_unreclaimable)
                                continue;
-                       if (nr_slab == 0 && zone->pages_scanned >=
-                                       (zone_reclaimable_pages(zone) * 6))
-                                       zone_set_flag(zone,
-                                                     ZONE_ALL_UNRECLAIMABLE);
+                       if (nr_slab == 0 &&
+                           zone->pages_scanned >= (zone_reclaimable_pages(zone) * 6))
+                               zone->all_unreclaimable = 1;
                        /*
                         * If we've done a decent amount of scanning and
                         * the reclaim ratio is low, start doing writepage
@@ -2102,13 +2134,18 @@ loop_again:
                            total_scanned > sc.nr_reclaimed + sc.nr_reclaimed / 2)
                                sc.may_writepage = 1;
 
-                       /*
-                        * We are still under min water mark. it mean we have
-                        * GFP_ATOMIC allocation failure risk. Hurry up!
-                        */
-                       if (!zone_watermark_ok(zone, order, min_wmark_pages(zone),
-                                             end_zone, 0))
-                               has_under_min_watermark_zone = 1;
+                       if (!zone_watermark_ok(zone, order,
+                                       high_wmark_pages(zone), end_zone, 0)) {
+                               all_zones_ok = 0;
+                               /*
+                                * We are still under min water mark.  This
+                                * means that we have a GFP_ATOMIC allocation
+                                * failure risk. Hurry up!
+                                */
+                               if (!zone_watermark_ok(zone, order,
+                                           min_wmark_pages(zone), end_zone, 0))
+                                       has_under_min_watermark_zone = 1;
+                       }
 
                }
                if (all_zones_ok)
@@ -2550,6 +2587,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
         * and RECLAIM_SWAP.
         */
        p->flags |= PF_MEMALLOC | PF_SWAPWRITE;
+       lockdep_set_current_reclaim_state(gfp_mask);
        reclaim_state.reclaimed_slab = 0;
        p->reclaim_state = &reclaim_state;
 
@@ -2593,6 +2631,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
 
        p->reclaim_state = NULL;
        current->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE);
+       lockdep_clear_current_reclaim_state();
        return sc.nr_reclaimed >= nr_pages;
 }
 
@@ -2615,7 +2654,7 @@ int zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order)
            zone_page_state(zone, NR_SLAB_RECLAIMABLE) <= zone->min_slab_pages)
                return ZONE_RECLAIM_FULL;
 
-       if (zone_is_all_unreclaimable(zone))
+       if (zone->all_unreclaimable)
                return ZONE_RECLAIM_FULL;
 
        /*
index fc5aa183bc4574a1f52247590fc21e4b20efefa4..7f760cbc73f35a7b49944fe86f046e5269850b31 100644 (file)
@@ -763,7 +763,7 @@ static void zoneinfo_show_print(struct seq_file *m, pg_data_t *pgdat,
                   "\n  prev_priority:     %i"
                   "\n  start_pfn:         %lu"
                   "\n  inactive_ratio:    %u",
-                          zone_is_all_unreclaimable(zone),
+                  zone->all_unreclaimable,
                   zone->prev_priority,
                   zone->zone_start_pfn,
                   zone->inactive_ratio);
index 09d4f1e2e4a8cef1974929198e8df452597c40f6..bde9f3d38c579eca51183cf38cb7f33effad8055 100644 (file)
@@ -46,6 +46,7 @@ enum {
        Opt_msize,
        Opt_trans,
        Opt_legacy,
+       Opt_version,
        Opt_err,
 };
 
@@ -53,9 +54,42 @@ static const match_table_t tokens = {
        {Opt_msize, "msize=%u"},
        {Opt_legacy, "noextend"},
        {Opt_trans, "trans=%s"},
+       {Opt_version, "version=%s"},
        {Opt_err, NULL},
 };
 
+inline int p9_is_proto_dotl(struct p9_client *clnt)
+{
+       return (clnt->proto_version == p9_proto_2010L);
+}
+EXPORT_SYMBOL(p9_is_proto_dotl);
+
+inline int p9_is_proto_dotu(struct p9_client *clnt)
+{
+       return (clnt->proto_version == p9_proto_2000u);
+}
+EXPORT_SYMBOL(p9_is_proto_dotu);
+
+/* Interpret mount option for protocol version */
+static unsigned char get_protocol_version(const substring_t *name)
+{
+       unsigned char version = -EINVAL;
+       if (!strncmp("9p2000", name->from, name->to-name->from)) {
+               version = p9_proto_legacy;
+               P9_DPRINTK(P9_DEBUG_9P, "Protocol version: Legacy\n");
+       } else if (!strncmp("9p2000.u", name->from, name->to-name->from)) {
+               version = p9_proto_2000u;
+               P9_DPRINTK(P9_DEBUG_9P, "Protocol version: 9P2000.u\n");
+       } else if (!strncmp("9p2010.L", name->from, name->to-name->from)) {
+               version = p9_proto_2010L;
+               P9_DPRINTK(P9_DEBUG_9P, "Protocol version: 9P2010.L\n");
+       } else {
+               P9_DPRINTK(P9_DEBUG_ERROR, "Unknown protocol version %s. ",
+                                                       name->from);
+       }
+       return version;
+}
+
 static struct p9_req_t *
 p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...);
 
@@ -75,7 +109,7 @@ static int parse_opts(char *opts, struct p9_client *clnt)
        int option;
        int ret = 0;
 
-       clnt->dotu = 1;
+       clnt->proto_version = p9_proto_2000u;
        clnt->msize = 8192;
 
        if (!opts)
@@ -118,7 +152,13 @@ static int parse_opts(char *opts, struct p9_client *clnt)
                        }
                        break;
                case Opt_legacy:
-                       clnt->dotu = 0;
+                       clnt->proto_version = p9_proto_legacy;
+                       break;
+               case Opt_version:
+                       ret = get_protocol_version(&args[0]);
+                       if (ret == -EINVAL)
+                               goto free_and_return;
+                       clnt->proto_version = ret;
                        break;
                default:
                        continue;
@@ -410,14 +450,15 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
                int ecode;
                char *ename;
 
-               err = p9pdu_readf(req->rc, c->dotu, "s?d", &ename, &ecode);
+               err = p9pdu_readf(req->rc, c->proto_version, "s?d",
+                                                       &ename, &ecode);
                if (err) {
                        P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n",
                                                                        err);
                        return err;
                }
 
-               if (c->dotu)
+               if (p9_is_proto_dotu(c))
                        err = -ecode;
 
                if (!err || !IS_ERR_VALUE(err))
@@ -515,7 +556,7 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
        /* marshall the data */
        p9pdu_prepare(req->tc, tag, type);
        va_start(ap, fmt);
-       err = p9pdu_vwritef(req->tc, c->dotu, fmt, ap);
+       err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap);
        va_end(ap);
        p9pdu_finalize(req->tc);
 
@@ -627,14 +668,31 @@ int p9_client_version(struct p9_client *c)
        char *version;
        int msize;
 
-       P9_DPRINTK(P9_DEBUG_9P, ">>> TVERSION msize %d extended %d\n",
-                                                       c->msize, c->dotu);
-       req = p9_client_rpc(c, P9_TVERSION, "ds", c->msize,
-                               c->dotu ? "9P2000.u" : "9P2000");
+       P9_DPRINTK(P9_DEBUG_9P, ">>> TVERSION msize %d protocol %d\n",
+                                               c->msize, c->proto_version);
+
+       switch (c->proto_version) {
+       case p9_proto_2010L:
+               req = p9_client_rpc(c, P9_TVERSION, "ds",
+                                       c->msize, "9P2010.L");
+               break;
+       case p9_proto_2000u:
+               req = p9_client_rpc(c, P9_TVERSION, "ds",
+                                       c->msize, "9P2000.u");
+               break;
+       case p9_proto_legacy:
+               req = p9_client_rpc(c, P9_TVERSION, "ds",
+                                       c->msize, "9P2000");
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       err = p9pdu_readf(req->rc, c->dotu, "ds", &msize, &version);
+       err = p9pdu_readf(req->rc, c->proto_version, "ds", &msize, &version);
        if (err) {
                P9_DPRINTK(P9_DEBUG_9P, "version error %d\n", err);
                p9pdu_dump(1, req->rc);
@@ -642,10 +700,12 @@ int p9_client_version(struct p9_client *c)
        }
 
        P9_DPRINTK(P9_DEBUG_9P, "<<< RVERSION msize %d %s\n", msize, version);
-       if (!memcmp(version, "9P2000.u", 8))
-               c->dotu = 1;
-       else if (!memcmp(version, "9P2000", 6))
-               c->dotu = 0;
+       if (!strncmp(version, "9P2010.L", 8))
+               c->proto_version = p9_proto_2010L;
+       else if (!strncmp(version, "9P2000.u", 8))
+               c->proto_version = p9_proto_2000u;
+       else if (!strncmp(version, "9P2000", 6))
+               c->proto_version = p9_proto_legacy;
        else {
                err = -EREMOTEIO;
                goto error;
@@ -700,8 +760,8 @@ struct p9_client *p9_client_create(const char *dev_name, char *options)
                goto put_trans;
        }
 
-       P9_DPRINTK(P9_DEBUG_MUX, "clnt %p trans %p msize %d dotu %d\n",
-               clnt, clnt->trans_mod, clnt->msize, clnt->dotu);
+       P9_DPRINTK(P9_DEBUG_MUX, "clnt %p trans %p msize %d protocol %d\n",
+               clnt, clnt->trans_mod, clnt->msize, clnt->proto_version);
 
        err = clnt->trans_mod->create(clnt, dev_name, options);
        if (err)
@@ -784,7 +844,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "Q", &qid);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "Q", &qid);
        if (err) {
                p9pdu_dump(1, req->rc);
                p9_free_req(clnt, req);
@@ -833,7 +893,7 @@ p9_client_auth(struct p9_client *clnt, char *uname, u32 n_uname, char *aname)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "Q", &qid);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "Q", &qid);
        if (err) {
                p9pdu_dump(1, req->rc);
                p9_free_req(clnt, req);
@@ -891,7 +951,7 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "R", &nwqids, &wqids);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "R", &nwqids, &wqids);
        if (err) {
                p9pdu_dump(1, req->rc);
                p9_free_req(clnt, req);
@@ -952,7 +1012,7 @@ int p9_client_open(struct p9_fid *fid, int mode)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "Qd", &qid, &iounit);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "Qd", &qid, &iounit);
        if (err) {
                p9pdu_dump(1, req->rc);
                goto free_and_error;
@@ -997,7 +1057,7 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "Qd", &qid, &iounit);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "Qd", &qid, &iounit);
        if (err) {
                p9pdu_dump(1, req->rc);
                goto free_and_error;
@@ -1098,7 +1158,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "D", &count, &dataptr);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr);
        if (err) {
                p9pdu_dump(1, req->rc);
                goto free_and_error;
@@ -1159,7 +1219,7 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "d", &count);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "d", &count);
        if (err) {
                p9pdu_dump(1, req->rc);
                goto free_and_error;
@@ -1199,7 +1259,7 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid)
                goto error;
        }
 
-       err = p9pdu_readf(req->rc, clnt->dotu, "wS", &ignored, ret);
+       err = p9pdu_readf(req->rc, clnt->proto_version, "wS", &ignored, ret);
        if (err) {
                p9pdu_dump(1, req->rc);
                p9_free_req(clnt, req);
@@ -1226,7 +1286,7 @@ error:
 }
 EXPORT_SYMBOL(p9_client_stat);
 
-static int p9_client_statsize(struct p9_wstat *wst, int optional)
+static int p9_client_statsize(struct p9_wstat *wst, int proto_version)
 {
        int ret;
 
@@ -1245,7 +1305,7 @@ static int p9_client_statsize(struct p9_wstat *wst, int optional)
        if (wst->muid)
                ret += strlen(wst->muid);
 
-       if (optional) {
+       if (proto_version == p9_proto_2000u) {
                ret += 2+4+4+4; /* extension[s] n_uid[4] n_gid[4] n_muid[4] */
                if (wst->extension)
                        ret += strlen(wst->extension);
@@ -1262,7 +1322,7 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst)
 
        err = 0;
        clnt = fid->clnt;
-       wst->size = p9_client_statsize(wst, clnt->dotu);
+       wst->size = p9_client_statsize(wst, clnt->proto_version);
        P9_DPRINTK(P9_DEBUG_9P, ">>> TWSTAT fid %d\n", fid->fid);
        P9_DPRINTK(P9_DEBUG_9P,
                "     sz=%x type=%x dev=%x qid=%x.%llx.%x\n"
index fc70147c771e15c2542a92e7286560f7faa1b98a..94f5a8f65e9c3649c8417a62c0c87b1e1ef708cd 100644 (file)
@@ -52,7 +52,7 @@
 #endif
 
 static int
-p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...);
+p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
 
 #ifdef CONFIG_NET_9P_DEBUG
 void
@@ -144,7 +144,8 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
 */
 
 static int
-p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
+p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
+       va_list ap)
 {
        const char *ptr;
        int errcode = 0;
@@ -194,7 +195,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int16_t len;
                                int size;
 
-                               errcode = p9pdu_readf(pdu, optional, "w", &len);
+                               errcode = p9pdu_readf(pdu, proto_version,
+                                                               "w", &len);
                                if (errcode)
                                        break;
 
@@ -217,7 +219,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                struct p9_qid *qid =
                                    va_arg(ap, struct p9_qid *);
 
-                               errcode = p9pdu_readf(pdu, optional, "bdq",
+                               errcode = p9pdu_readf(pdu, proto_version, "bdq",
                                                      &qid->type, &qid->version,
                                                      &qid->path);
                        }
@@ -230,7 +232,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
                                                                        -1;
                                errcode =
-                                   p9pdu_readf(pdu, optional,
+                                   p9pdu_readf(pdu, proto_version,
                                                "wwdQdddqssss?sddd",
                                                &stbuf->size, &stbuf->type,
                                                &stbuf->dev, &stbuf->qid,
@@ -250,7 +252,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                void **data = va_arg(ap, void **);
 
                                errcode =
-                                   p9pdu_readf(pdu, optional, "d", count);
+                                   p9pdu_readf(pdu, proto_version, "d", count);
                                if (!errcode) {
                                        *count =
                                            MIN(*count,
@@ -263,8 +265,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int16_t *nwname = va_arg(ap, int16_t *);
                                char ***wnames = va_arg(ap, char ***);
 
-                               errcode =
-                                   p9pdu_readf(pdu, optional, "w", nwname);
+                               errcode = p9pdu_readf(pdu, proto_version,
+                                                               "w", nwname);
                                if (!errcode) {
                                        *wnames =
                                            kmalloc(sizeof(char *) * *nwname,
@@ -278,7 +280,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
 
                                        for (i = 0; i < *nwname; i++) {
                                                errcode =
-                                                   p9pdu_readf(pdu, optional,
+                                                   p9pdu_readf(pdu,
+                                                               proto_version,
                                                                "s",
                                                                &(*wnames)[i]);
                                                if (errcode)
@@ -306,7 +309,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                *wqids = NULL;
 
                                errcode =
-                                   p9pdu_readf(pdu, optional, "w", nwqid);
+                                   p9pdu_readf(pdu, proto_version, "w", nwqid);
                                if (!errcode) {
                                        *wqids =
                                            kmalloc(*nwqid *
@@ -321,7 +324,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
 
                                        for (i = 0; i < *nwqid; i++) {
                                                errcode =
-                                                   p9pdu_readf(pdu, optional,
+                                                   p9pdu_readf(pdu,
+                                                               proto_version,
                                                                "Q",
                                                                &(*wqids)[i]);
                                                if (errcode)
@@ -336,7 +340,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                        }
                        break;
                case '?':
-                       if (!optional)
+                       if (proto_version != p9_proto_2000u)
                                return 0;
                        break;
                default:
@@ -352,7 +356,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
 }
 
 int
-p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
+p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
+       va_list ap)
 {
        const char *ptr;
        int errcode = 0;
@@ -389,7 +394,8 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                if (sptr)
                                        len = MIN(strlen(sptr), USHORT_MAX);
 
-                               errcode = p9pdu_writef(pdu, optional, "w", len);
+                               errcode = p9pdu_writef(pdu, proto_version,
+                                                               "w", len);
                                if (!errcode && pdu_write(pdu, sptr, len))
                                        errcode = -EFAULT;
                        }
@@ -398,7 +404,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                const struct p9_qid *qid =
                                    va_arg(ap, const struct p9_qid *);
                                errcode =
-                                   p9pdu_writef(pdu, optional, "bdq",
+                                   p9pdu_writef(pdu, proto_version, "bdq",
                                                 qid->type, qid->version,
                                                 qid->path);
                        } break;
@@ -406,7 +412,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                const struct p9_wstat *stbuf =
                                    va_arg(ap, const struct p9_wstat *);
                                errcode =
-                                   p9pdu_writef(pdu, optional,
+                                   p9pdu_writef(pdu, proto_version,
                                                 "wwdQdddqssss?sddd",
                                                 stbuf->size, stbuf->type,
                                                 stbuf->dev, &stbuf->qid,
@@ -421,8 +427,8 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int32_t count = va_arg(ap, int32_t);
                                const void *data = va_arg(ap, const void *);
 
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "d", count);
+                               errcode = p9pdu_writef(pdu, proto_version, "d",
+                                                                       count);
                                if (!errcode && pdu_write(pdu, data, count))
                                        errcode = -EFAULT;
                        }
@@ -431,8 +437,8 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int32_t count = va_arg(ap, int32_t);
                                const char __user *udata =
                                                va_arg(ap, const void __user *);
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "d", count);
+                               errcode = p9pdu_writef(pdu, proto_version, "d",
+                                                                       count);
                                if (!errcode && pdu_write_u(pdu, udata, count))
                                        errcode = -EFAULT;
                        }
@@ -441,14 +447,15 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int16_t nwname = va_arg(ap, int);
                                const char **wnames = va_arg(ap, const char **);
 
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "w", nwname);
+                               errcode = p9pdu_writef(pdu, proto_version, "w",
+                                                                       nwname);
                                if (!errcode) {
                                        int i;
 
                                        for (i = 0; i < nwname; i++) {
                                                errcode =
-                                                   p9pdu_writef(pdu, optional,
+                                                   p9pdu_writef(pdu,
+                                                               proto_version,
                                                                 "s",
                                                                 wnames[i]);
                                                if (errcode)
@@ -462,14 +469,15 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                struct p9_qid *wqids =
                                    va_arg(ap, struct p9_qid *);
 
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "w", nwqid);
+                               errcode = p9pdu_writef(pdu, proto_version, "w",
+                                                                       nwqid);
                                if (!errcode) {
                                        int i;
 
                                        for (i = 0; i < nwqid; i++) {
                                                errcode =
-                                                   p9pdu_writef(pdu, optional,
+                                                   p9pdu_writef(pdu,
+                                                               proto_version,
                                                                 "Q",
                                                                 &wqids[i]);
                                                if (errcode)
@@ -479,7 +487,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                        }
                        break;
                case '?':
-                       if (!optional)
+                       if (proto_version != p9_proto_2000u)
                                return 0;
                        break;
                default:
@@ -494,32 +502,32 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
        return errcode;
 }
 
-int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...)
+int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 {
        va_list ap;
        int ret;
 
        va_start(ap, fmt);
-       ret = p9pdu_vreadf(pdu, optional, fmt, ap);
+       ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
        va_end(ap);
 
        return ret;
 }
 
 static int
-p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...)
+p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 {
        va_list ap;
        int ret;
 
        va_start(ap, fmt);
-       ret = p9pdu_vwritef(pdu, optional, fmt, ap);
+       ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
        va_end(ap);
 
        return ret;
 }
 
-int p9stat_read(char *buf, int len, struct p9_wstat *st, int dotu)
+int p9stat_read(char *buf, int len, struct p9_wstat *st, int proto_version)
 {
        struct p9_fcall fake_pdu;
        int ret;
@@ -529,7 +537,7 @@ int p9stat_read(char *buf, int len, struct p9_wstat *st, int dotu)
        fake_pdu.sdata = buf;
        fake_pdu.offset = 0;
 
-       ret = p9pdu_readf(&fake_pdu, dotu, "S", st);
+       ret = p9pdu_readf(&fake_pdu, proto_version, "S", st);
        if (ret) {
                P9_DPRINTK(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
                p9pdu_dump(1, &fake_pdu);
index ccde462e7ac57173fa4eb8aea0131d6225357fef..2431c0f38d56784c02f42e077fcaa990e523882c 100644 (file)
@@ -25,9 +25,9 @@
  *
  */
 
-int
-p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap);
-int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...);
+int p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
+                                                               va_list ap);
+int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
 int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type);
 int p9pdu_finalize(struct p9_fcall *pdu);
 void p9pdu_dump(int, struct p9_fcall *);
index cb50f4ae5eefa1f5cde720f43daaf6f19e9034eb..0aaed48193795407f87c3a9ecbdf1b7f2869a8b2 100644 (file)
@@ -49,8 +49,6 @@
 
 /* a single mutex to manage channel initialization and attachment */
 static DEFINE_MUTEX(virtio_9p_lock);
-/* global which tracks highest initialized channel */
-static int chan_index;
 
 /**
  * struct virtio_chan - per-instance transport information
@@ -68,8 +66,7 @@ static int chan_index;
  *
  */
 
-static struct virtio_chan {
-       bool initialized;
+struct virtio_chan {
        bool inuse;
 
        spinlock_t lock;
@@ -80,7 +77,11 @@ static struct virtio_chan {
 
        /* Scatterlist: can be too big for stack. */
        struct scatterlist sg[VIRTQUEUE_NUM];
-} channels[MAX_9P_CHAN];
+
+       struct list_head chan_list;
+};
+
+static struct list_head virtio_chan_list;
 
 /* How many bytes left in this page. */
 static unsigned int rest_of_page(void *data)
@@ -217,9 +218,7 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
  * p9_virtio_probe - probe for existence of 9P virtio channels
  * @vdev: virtio device to probe
  *
- * This probes for existing virtio channels.  At present only
- * a single channel is in use, so in the future more work may need
- * to be done here.
+ * This probes for existing virtio channels.
  *
  */
 
@@ -227,16 +226,10 @@ static int p9_virtio_probe(struct virtio_device *vdev)
 {
        int err;
        struct virtio_chan *chan;
-       int index;
 
-       mutex_lock(&virtio_9p_lock);
-       index = chan_index++;
-       chan = &channels[index];
-       mutex_unlock(&virtio_9p_lock);
-
-       if (chan_index > MAX_9P_CHAN) {
-               printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n");
-               BUG();
+       chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL);
+       if (!chan) {
+               printk(KERN_ERR "9p: Failed to allocate virtio 9P channel\n");
                err = -ENOMEM;
                goto fail;
        }
@@ -255,15 +248,15 @@ static int p9_virtio_probe(struct virtio_device *vdev)
        sg_init_table(chan->sg, VIRTQUEUE_NUM);
 
        chan->inuse = false;
-       chan->initialized = true;
+       mutex_lock(&virtio_9p_lock);
+       list_add_tail(&chan->chan_list, &virtio_chan_list);
+       mutex_unlock(&virtio_9p_lock);
        return 0;
 
 out_free_vq:
        vdev->config->del_vqs(vdev);
+       kfree(chan);
 fail:
-       mutex_lock(&virtio_9p_lock);
-       chan_index--;
-       mutex_unlock(&virtio_9p_lock);
        return err;
 }
 
@@ -280,35 +273,31 @@ fail:
  * We use a simple reference count mechanism to ensure that only a single
  * mount has a channel open at a time.
  *
- * Bugs: doesn't allow identification of a specific channel
- * to allocate, channels are allocated sequentially. This was
- * a pragmatic decision to get things rolling, but ideally some
- * way of identifying the channel to attach to would be nice
- * if we are going to support multiple channels.
- *
  */
 
 static int
 p9_virtio_create(struct p9_client *client, const char *devname, char *args)
 {
-       struct virtio_chan *chan = channels;
-       int index = 0;
+       struct virtio_chan *chan;
+       int ret = -ENOENT;
+       int found = 0;
 
        mutex_lock(&virtio_9p_lock);
-       while (index < MAX_9P_CHAN) {
-               if (chan->initialized && !chan->inuse) {
-                       chan->inuse = true;
-                       break;
-               } else {
-                       index++;
-                       chan = &channels[index];
+       list_for_each_entry(chan, &virtio_chan_list, chan_list) {
+               if (!strcmp(devname, dev_name(&chan->vdev->dev))) {
+                       if (!chan->inuse) {
+                               chan->inuse = true;
+                               found = 1;
+                               break;
+                       }
+                       ret = -EBUSY;
                }
        }
        mutex_unlock(&virtio_9p_lock);
 
-       if (index >= MAX_9P_CHAN) {
+       if (!found) {
                printk(KERN_ERR "9p: no channels available\n");
-               return -ENODEV;
+               return ret;
        }
 
        client->trans = (void *)chan;
@@ -329,11 +318,13 @@ static void p9_virtio_remove(struct virtio_device *vdev)
        struct virtio_chan *chan = vdev->priv;
 
        BUG_ON(chan->inuse);
+       vdev->config->del_vqs(vdev);
+
+       mutex_lock(&virtio_9p_lock);
+       list_del(&chan->chan_list);
+       mutex_unlock(&virtio_9p_lock);
+       kfree(chan);
 
-       if (chan->initialized) {
-               vdev->config->del_vqs(vdev);
-               chan->initialized = false;
-       }
 }
 
 static struct virtio_device_id id_table[] = {
@@ -364,10 +355,7 @@ static struct p9_trans_module p9_virtio_trans = {
 /* The standard init function */
 static int __init p9_virtio_init(void)
 {
-       int count;
-
-       for (count = 0; count < MAX_9P_CHAN; count++)
-               channels[count].initialized = false;
+       INIT_LIST_HEAD(&virtio_chan_list);
 
        v9fs_register_trans(&p9_virtio_trans);
        return register_virtio_driver(&p9_virtio_drv);
index 6dcdd2517819ea2a170038e1d21b35a193606c68..f845d9d72f7307e3910fe39d0b32fbb95491114f 100644 (file)
@@ -71,8 +71,9 @@ static size_t rpc_ntop6(const struct sockaddr *sap,
        if (unlikely(len == 0))
                return len;
 
-       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
-           !(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_SITELOCAL))
+       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
+               return len;
+       if (sin6->sin6_scope_id == 0)
                return len;
 
        rc = snprintf(scopebuf, sizeof(scopebuf), "%c%u",
@@ -165,8 +166,7 @@ static int rpc_parse_scope_id(const char *buf, const size_t buflen,
        if (*delim != IPV6_SCOPE_DELIMITER)
                return 0;
 
-       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
-           !(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_SITELOCAL))
+       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
                return 0;
 
        len = (buf + buflen) - delim - 1;
index f7a7f8380e385565034cb70f7f9a3dfdcf37bef2..0cfccc2a02979c4844c63dffbbdd47312a0139e6 100644 (file)
@@ -206,8 +206,14 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct
        ctx->gc_win = window_size;
        /* gssd signals an error by passing ctx->gc_win = 0: */
        if (ctx->gc_win == 0) {
-               /* in which case, p points to  an error code which we ignore */
-               p = ERR_PTR(-EACCES);
+               /*
+                * in which case, p points to an error code. Anything other
+                * than -EKEYEXPIRED gets converted to -EACCES.
+                */
+               p = simple_get_bytes(p, end, &ret, sizeof(ret));
+               if (!IS_ERR(p))
+                       p = (ret == -EKEYEXPIRED) ? ERR_PTR(-EKEYEXPIRED) :
+                                                   ERR_PTR(-EACCES);
                goto err;
        }
        /* copy the opaque wire context */
@@ -646,6 +652,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
                err = PTR_ERR(p);
                switch (err) {
                case -EACCES:
+               case -EKEYEXPIRED:
                        gss_msg->msg.errno = err;
                        err = mlen;
                        break;
index 9ea45383480ee4ddc65711b0c3b45016ff048f0b..8d63f8fd29b7e3ff0e940d7457d45203f99c56b1 100644 (file)
@@ -999,19 +999,14 @@ rpc_fill_super(struct super_block *sb, void *data, int silent)
        inode = rpc_get_inode(sb, S_IFDIR | 0755);
        if (!inode)
                return -ENOMEM;
-       root = d_alloc_root(inode);
+       sb->s_root = root = d_alloc_root(inode);
        if (!root) {
                iput(inode);
                return -ENOMEM;
        }
        if (rpc_populate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF, NULL))
-               goto out;
-       sb->s_root = root;
+               return -ENOMEM;
        return 0;
-out:
-       d_genocide(root);
-       dput(root);
-       return -ENOMEM;
 }
 
 static int
index 538ca433a56cc72e2bae671e2b74174cb8efb3a8..8420a4205b76bc9278e247ee299d10314c68558a 100644 (file)
@@ -133,7 +133,7 @@ svc_pool_map_choose_mode(void)
                return SVC_POOL_PERNODE;
        }
 
-       node = any_online_node(node_online_map);
+       node = first_online_node;
        if (nr_cpus_node(node) > 2) {
                /*
                 * Non-trivial SMP, or CONFIG_NUMA on
@@ -506,6 +506,10 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size)
 {
        unsigned int pages, arghi;
 
+       /* bc_xprt uses fore channel allocated buffers */
+       if (svc_is_backchannel(rqstp))
+               return 1;
+
        pages = size / PAGE_SIZE + 1; /* extra page as we hold both request and reply.
                                       * We assume one is at most one page
                                       */
index 7d1f9e928f69b23ff640643c06c0b8b86f95b80b..8f0f1fb3dc5259419a1e823f522d9e5792538831 100644 (file)
@@ -173,11 +173,13 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
                .sin_addr.s_addr        = htonl(INADDR_ANY),
                .sin_port               = htons(port),
        };
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        struct sockaddr_in6 sin6 = {
                .sin6_family            = AF_INET6,
                .sin6_addr              = IN6ADDR_ANY_INIT,
                .sin6_port              = htons(port),
        };
+#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
        struct sockaddr *sap;
        size_t len;
 
@@ -186,10 +188,12 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
                sap = (struct sockaddr *)&sin;
                len = sizeof(sin);
                break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case PF_INET6:
                sap = (struct sockaddr *)&sin6;
                len = sizeof(sin6);
                break;
+#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
        default:
                return ERR_PTR(-EAFNOSUPPORT);
        }
@@ -231,7 +235,10 @@ int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
  err:
        spin_unlock(&svc_xprt_class_lock);
        dprintk("svc: transport %s not found\n", xprt_name);
-       return -ENOENT;
+
+       /* This errno is exposed to user space.  Provide a reasonable
+        * perror msg for a bad transport. */
+       return -EPROTONOSUPPORT;
 }
 EXPORT_SYMBOL_GPL(svc_create_xprt);
 
@@ -699,8 +706,10 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
        spin_unlock_bh(&pool->sp_lock);
 
        len = 0;
-       if (test_bit(XPT_LISTENER, &xprt->xpt_flags) &&
-           !test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
+       if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
+               dprintk("svc_recv: found XPT_CLOSE\n");
+               svc_delete_xprt(xprt);
+       } else if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) {
                struct svc_xprt *newxpt;
                newxpt = xprt->xpt_ops->xpo_accept(xprt);
                if (newxpt) {
@@ -726,7 +735,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
                        svc_xprt_received(newxpt);
                }
                svc_xprt_received(xprt);
-       } else if (!test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
+       } else {
                dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",
                        rqstp, pool->sp_id, xprt,
                        atomic_read(&xprt->xpt_ref.refcount));
@@ -739,11 +748,6 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
                dprintk("svc: got len=%d\n", len);
        }
 
-       if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
-               dprintk("svc_recv: found XPT_CLOSE\n");
-               svc_delete_xprt(xprt);
-       }
-
        /* No data, incomplete (TCP) read, or accept() */
        if (len == 0 || len == -EAGAIN) {
                rqstp->rq_res.len = 0;
@@ -889,11 +893,8 @@ void svc_delete_xprt(struct svc_xprt *xprt)
        if (test_bit(XPT_TEMP, &xprt->xpt_flags))
                serv->sv_tmpcnt--;
 
-       for (dr = svc_deferred_dequeue(xprt); dr;
-            dr = svc_deferred_dequeue(xprt)) {
-               svc_xprt_put(xprt);
+       while ((dr = svc_deferred_dequeue(xprt)) != NULL)
                kfree(dr);
-       }
 
        svc_xprt_put(xprt);
        spin_unlock_bh(&serv->sv_lock);
index d8c0411144978c22bfded676d3303b260bf84897..afdcb0459a8319660443b0130dc000d65e0f5449 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #define RPCDBG_FACILITY        RPCDBG_AUTH
 
+#include <linux/sunrpc/clnt.h>
 
 /*
  * AUTHUNIX and AUTHNULL credentials are both handled here.
@@ -187,10 +188,13 @@ static int ip_map_parse(struct cache_detail *cd,
         * for scratch: */
        char *buf = mesg;
        int len;
-       int b1, b2, b3, b4, b5, b6, b7, b8;
-       char c;
        char class[8];
-       struct in6_addr addr;
+       union {
+               struct sockaddr         sa;
+               struct sockaddr_in      s4;
+               struct sockaddr_in6     s6;
+       } address;
+       struct sockaddr_in6 sin6;
        int err;
 
        struct ip_map *ipmp;
@@ -209,24 +213,24 @@ static int ip_map_parse(struct cache_detail *cd,
        len = qword_get(&mesg, buf, mlen);
        if (len <= 0) return -EINVAL;
 
-       if (sscanf(buf, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) == 4) {
-               addr.s6_addr32[0] = 0;
-               addr.s6_addr32[1] = 0;
-               addr.s6_addr32[2] = htonl(0xffff);
-               addr.s6_addr32[3] =
-                       htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);
-       } else if (sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x%c",
-                       &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &c) == 8) {
-               addr.s6_addr16[0] = htons(b1);
-               addr.s6_addr16[1] = htons(b2);
-               addr.s6_addr16[2] = htons(b3);
-               addr.s6_addr16[3] = htons(b4);
-               addr.s6_addr16[4] = htons(b5);
-               addr.s6_addr16[5] = htons(b6);
-               addr.s6_addr16[6] = htons(b7);
-               addr.s6_addr16[7] = htons(b8);
-       } else
+       if (rpc_pton(buf, len, &address.sa, sizeof(address)) == 0)
                return -EINVAL;
+       switch (address.sa.sa_family) {
+       case AF_INET:
+               /* Form a mapped IPv4 address in sin6 */
+               memset(&sin6, 0, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
+               sin6.sin6_addr.s6_addr32[3] = address.s4.sin_addr.s_addr;
+               break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       case AF_INET6:
+               memcpy(&sin6, &address.s6, sizeof(sin6));
+               break;
+#endif
+       default:
+               return -EINVAL;
+       }
 
        expiry = get_expiry(&mesg);
        if (expiry ==0)
@@ -243,7 +247,8 @@ static int ip_map_parse(struct cache_detail *cd,
        } else
                dom = NULL;
 
-       ipmp = ip_map_lookup(class, &addr);
+       /* IPv6 scope IDs are ignored for now */
+       ipmp = ip_map_lookup(class, &sin6.sin6_addr);
        if (ipmp) {
                err = ip_map_update(ipmp,
                             container_of(dom, struct unix_domain, h),
@@ -619,7 +624,7 @@ static int unix_gid_show(struct seq_file *m,
        else
                glen = 0;
 
-       seq_printf(m, "%d %d:", ug->uid, glen);
+       seq_printf(m, "%u %d:", ug->uid, glen);
        for (i = 0; i < glen; i++)
                seq_printf(m, " %d", GROUP_AT(ug->gi, i));
        seq_printf(m, "\n");
index 870929e08e5de7463b77c7b760afe43e3c382888..a29f259204e69d190a334bc0f1dc77bd1de6043a 100644 (file)
@@ -968,6 +968,7 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp)
        return len;
  err_delete:
        set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags);
+       svc_xprt_received(&svsk->sk_xprt);
  err_again:
        return -EAGAIN;
 }
@@ -1357,7 +1358,7 @@ int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
 
        if (!so)
                return err;
-       if (so->sk->sk_family != AF_INET)
+       if ((so->sk->sk_family != PF_INET) && (so->sk->sk_family != PF_INET6))
                err =  -EAFNOSUPPORT;
        else if (so->sk->sk_protocol != IPPROTO_TCP &&
            so->sk->sk_protocol != IPPROTO_UDP)
index 3d739e5d15d8b65a77be7fcb80a9ed012ba7c0d3..712412982ceef0b8e7c0eb0752fe50b97e133294 100644 (file)
@@ -1912,6 +1912,11 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
        case -EALREADY:
                xprt_clear_connecting(xprt);
                return;
+       case -EINVAL:
+               /* Happens, for instance, if the user specified a link
+                * local IPv6 address without a scope-id.
+                */
+               goto out;
        }
 out_eagain:
        status = -EAGAIN;
@@ -2100,7 +2105,7 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
  * we allocate pages instead doing a kmalloc like rpc_malloc is because we want
  * to use the server side send routines.
  */
-void *bc_malloc(struct rpc_task *task, size_t size)
+static void *bc_malloc(struct rpc_task *task, size_t size)
 {
        struct page *page;
        struct rpc_buffer *buf;
@@ -2120,7 +2125,7 @@ void *bc_malloc(struct rpc_task *task, size_t size)
 /*
  * Free the space allocated in the bc_alloc routine
  */
-void bc_free(void *buffer)
+static void bc_free(void *buffer)
 {
        struct rpc_buffer *buf;
 
index 3257d3d96767da60103c3e7d91beef537541f3cc..a4d74344d805610a67e0e7e0bbf1674875f555a7 100755 (executable)
@@ -145,11 +145,14 @@ our $Sparse       = qr{
                        __kprobes|
                        __ref
                }x;
+
+# Notes to $Attribute:
+# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
 our $Attribute = qr{
                        const|
                        __read_mostly|
                        __kprobes|
-                       __(?:mem|cpu|dev|)(?:initdata|init)|
+                       __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)|
                        ____cacheline_aligned|
                        ____cacheline_aligned_in_smp|
                        ____cacheline_internodealigned_in_smp|
@@ -189,6 +192,14 @@ our $typeTypedefs = qr{(?x:
        atomic_t
 )};
 
+our $logFunctions = qr{(?x:
+       printk|
+       pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)|
+       dev_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)|
+       WARN|
+       panic
+)};
+
 our @typeList = (
        qr{void},
        qr{(?:unsigned\s+)?char},
@@ -1377,12 +1388,17 @@ sub process {
 #80 column limit
                if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ &&
                    $rawline !~ /^.\s*\*\s*\@$Ident\s/ &&
-                   $line !~ /^\+\s*printk\s*\(\s*(?:KERN_\S+\s*)?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ &&
+                   $line !~ /^\+\s*$logFunctions\s*\(\s*(?:KERN_\S+\s*)?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ &&
                    $length > 80)
                {
                        WARN("line over 80 characters\n" . $herecurr);
                }
 
+# check for spaces before a quoted newline
+               if ($rawline =~ /^.*\".*\s\\n/) {
+                       WARN("unnecessary whitespace before a quoted newline\n" . $herecurr);
+               }
+
 # check for adding lines without a newline.
                if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
                        WARN("adding a line without newline at end of file\n" . $herecurr);
@@ -1411,6 +1427,12 @@ sub process {
                        ERROR("code indent should use tabs where possible\n" . $herevet);
                }
 
+# check for space before tabs.
+               if ($rawline =~ /^\+/ && $rawline =~ / \t/) {
+                       my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+                       WARN("please, no space before tabs\n" . $herevet);
+               }
+
 # check we are in a valid C source file if not then ignore this hunk
                next if ($realfile !~ /\.(h|c)$/);
 
@@ -2182,8 +2204,10 @@ sub process {
                                # Find out how long the conditional actually is.
                                my @newlines = ($c =~ /\n/gs);
                                my $cond_lines = 1 + $#newlines;
+                               my $stat_real = '';
 
-                               my $stat_real = raw_line($linenr, $cond_lines);
+                               $stat_real = raw_line($linenr, $cond_lines)
+                                                       . "\n" if ($cond_lines);
                                if (defined($stat_real) && $cond_lines > 1) {
                                        $stat_real = "[...]\n$stat_real";
                                }
@@ -2348,6 +2372,8 @@ sub process {
                                DECLARE_PER_CPU|
                                DEFINE_PER_CPU|
                                __typeof__\(|
+                               union|
+                               struct|
                                \.$Ident\s*=\s*|
                                ^\"|\"$
                        }x;
@@ -2572,6 +2598,11 @@ sub process {
                        WARN("plain inline is preferred over $1\n" . $herecurr);
                }
 
+# check for sizeof(&)
+               if ($line =~ /\bsizeof\s*\(\s*\&/) {
+                       WARN("sizeof(& should be avoided\n" . $herecurr);
+               }
+
 # check for new externs in .c files.
                if ($realfile =~ /\.c$/ && defined $stat &&
                    $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
@@ -2634,9 +2665,46 @@ sub process {
                if ($line =~ /^.\s*__initcall\s*\(/) {
                        WARN("please use device_initcall() instead of __initcall()\n" . $herecurr);
                }
-# check for struct file_operations, ensure they are const.
+# check for various ops structs, ensure they are const.
+               my $struct_ops = qr{acpi_dock_ops|
+                               address_space_operations|
+                               backlight_ops|
+                               block_device_operations|
+                               dentry_operations|
+                               dev_pm_ops|
+                               dma_map_ops|
+                               extent_io_ops|
+                               file_lock_operations|
+                               file_operations|
+                               hv_ops|
+                               ide_dma_ops|
+                               intel_dvo_dev_ops|
+                               item_operations|
+                               iwl_ops|
+                               kgdb_arch|
+                               kgdb_io|
+                               kset_uevent_ops|
+                               lock_manager_operations|
+                               microcode_ops|
+                               mtrr_ops|
+                               neigh_ops|
+                               nlmsvc_binding|
+                               pci_raw_ops|
+                               pipe_buf_operations|
+                               platform_hibernation_ops|
+                               platform_suspend_ops|
+                               proto_ops|
+                               rpc_pipe_ops|
+                               seq_operations|
+                               snd_ac97_build_ops|
+                               soc_pcmcia_socket_ops|
+                               stacktrace_ops|
+                               sysfs_ops|
+                               tty_operations|
+                               usb_mon_operations|
+                               wd_ops}x;
                if ($line !~ /\bconst\b/ &&
-                   $line =~ /\bstruct\s+(file_operations|seq_operations)\b/) {
+                   $line =~ /\bstruct\s+($struct_ops)\b/) {
                        WARN("struct $1 should normally be const\n" .
                                $herecurr);
                }
index 2f3230db7ffbfd3044ceba5888e14bd77dc23148..f76f3d13276dd114c880e13ec45cad7452f992b9 100755 (executable)
@@ -41,6 +41,8 @@ my $web = 0;
 my $subsystem = 0;
 my $status = 0;
 my $keywords = 1;
+my $sections = 0;
+my $file_emails = 0;
 my $from_filename = 0;
 my $pattern_depth = 0;
 my $version = 0;
@@ -120,9 +122,11 @@ if (!GetOptions(
                'web!' => \$web,
                'pattern-depth=i' => \$pattern_depth,
                'k|keywords!' => \$keywords,
+               'sections!' => \$sections,
+               'fe|file-emails!' => \$file_emails,
                'f|file' => \$from_filename,
                'v|version' => \$version,
-               'h|help' => \$help,
+               'h|help|usage' => \$help,
                )) {
     die "$P: invalid argument - use --help if necessary\n";
 }
@@ -137,9 +141,9 @@ if ($version != 0) {
     exit 0;
 }
 
-if ($#ARGV < 0) {
-    usage();
-    die "$P: argument missing: patchfile or -f file please\n";
+if (-t STDIN && !@ARGV) {
+    # We're talking to a terminal, but have no command line arguments.
+    die "$P: missing patchfile or -f file - use --help if necessary\n";
 }
 
 if ($output_separator ne ", ") {
@@ -150,16 +154,24 @@ if ($output_rolestats) {
     $output_roles = 1;
 }
 
-my $selections = $email + $scm + $status + $subsystem + $web;
-if ($selections == 0) {
-    usage();
-    die "$P:  Missing required option: email, scm, status, subsystem or web\n";
+if ($sections) {
+    $email = 0;
+    $email_list = 0;
+    $scm = 0;
+    $status = 0;
+    $subsystem = 0;
+    $web = 0;
+    $keywords = 0;
+} else {
+    my $selections = $email + $scm + $status + $subsystem + $web;
+    if ($selections == 0) {
+       die "$P:  Missing required option: email, scm, status, subsystem or web\n";
+    }
 }
 
 if ($email &&
     ($email_maintainer + $email_list + $email_subscriber_list +
      $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) {
-    usage();
     die "$P: Please select at least 1 email option\n";
 }
 
@@ -173,8 +185,9 @@ if (!top_of_kernel_tree($lk_path)) {
 my @typevalue = ();
 my %keyword_hash;
 
-open(MAINT, "<${lk_path}MAINTAINERS") || die "$P: Can't open MAINTAINERS\n";
-while (<MAINT>) {
+open (my $maint, '<', "${lk_path}MAINTAINERS")
+    or die "$P: Can't open MAINTAINERS: $!\n";
+while (<$maint>) {
     my $line = $_;
 
     if ($line =~ m/^(\C):\s*(.*)/) {
@@ -199,13 +212,14 @@ while (<MAINT>) {
        push(@typevalue, $line);
     }
 }
-close(MAINT);
+close($maint);
 
 my %mailmap;
 
 if ($email_remove_duplicates) {
-    open(MAILMAP, "<${lk_path}.mailmap") || warn "$P: Can't open .mailmap\n";
-    while (<MAILMAP>) {
+    open(my $mailmap, '<', "${lk_path}.mailmap")
+       or warn "$P: Can't open .mailmap: $!\n";
+    while (<$mailmap>) {
        my $line = $_;
 
        next if ($line =~ m/^\s*#/);
@@ -224,7 +238,7 @@ if ($email_remove_duplicates) {
            $mailmap{$name} = \@arr;
        }
     }
-    close(MAILMAP);
+    close($mailmap);
 }
 
 ## use the filenames on the command line or find the filenames in the patchfiles
@@ -232,31 +246,47 @@ if ($email_remove_duplicates) {
 my @files = ();
 my @range = ();
 my @keyword_tvi = ();
+my @file_emails = ();
+
+if (!@ARGV) {
+    push(@ARGV, "&STDIN");
+}
 
 foreach my $file (@ARGV) {
-    ##if $file is a directory and it lacks a trailing slash, add one
-    if ((-d $file)) {
-       $file =~ s@([^/])$@$1/@;
-    } elsif (!(-f $file)) {
-       die "$P: file '${file}' not found\n";
+    if ($file ne "&STDIN") {
+       ##if $file is a directory and it lacks a trailing slash, add one
+       if ((-d $file)) {
+           $file =~ s@([^/])$@$1/@;
+       } elsif (!(-f $file)) {
+           die "$P: file '${file}' not found\n";
+       }
     }
     if ($from_filename) {
        push(@files, $file);
-       if (-f $file && $keywords) {
-           open(FILE, "<$file") or die "$P: Can't open ${file}\n";
-           my $text = do { local($/) ; <FILE> };
-           foreach my $line (keys %keyword_hash) {
-               if ($text =~ m/$keyword_hash{$line}/x) {
-                   push(@keyword_tvi, $line);
+       if (-f $file && ($keywords || $file_emails)) {
+           open(my $f, '<', $file)
+               or die "$P: Can't open $file: $!\n";
+           my $text = do { local($/) ; <$f> };
+           close($f);
+           if ($keywords) {
+               foreach my $line (keys %keyword_hash) {
+                   if ($text =~ m/$keyword_hash{$line}/x) {
+                       push(@keyword_tvi, $line);
+                   }
                }
            }
-           close(FILE);
+           if ($file_emails) {
+               my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
+               push(@file_emails, clean_file_emails(@poss_addr));
+           }
        }
     } else {
        my $file_cnt = @files;
        my $lastfile;
-       open(PATCH, "<$file") or die "$P: Can't open ${file}\n";
-       while (<PATCH>) {
+
+       open(my $patch, '<', $file)
+           or die "$P: Can't open $file: $!\n";
+       while (<$patch>) {
            my $patch_line = $_;
            if (m/^\+\+\+\s+(\S+)/) {
                my $filename = $1;
@@ -276,7 +306,8 @@ foreach my $file (@ARGV) {
                }
            }
        }
-       close(PATCH);
+       close($patch);
+
        if ($file_cnt == @files) {
            warn "$P: file '${file}' doesn't appear to be a patch.  "
                . "Add -f to options?\n";
@@ -285,6 +316,8 @@ foreach my $file (@ARGV) {
     }
 }
 
+@file_emails = uniq(@file_emails);
+
 my @email_to = ();
 my @list_to = ();
 my @scm = ();
@@ -314,6 +347,7 @@ foreach my $file (@files) {
                if ($type eq 'X') {
                    if (file_match_pattern($file, $value)) {
                        $exclude = 1;
+                       last;
                    }
                }
            }
@@ -340,12 +374,28 @@ foreach my $file (@files) {
            }
        }
 
-       $tvi += ($end - $start);
-
+       $tvi = $end + 1;
     }
 
     foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
        add_categories($line);
+           if ($sections) {
+               my $i;
+               my $start = find_starting_index($line);
+               my $end = find_ending_index($line);
+               for ($i = $start; $i < $end; $i++) {
+                   my $line = $typevalue[$i];
+                   if ($line =~ /^[FX]:/) {            ##Restore file patterns
+                       $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
+                       $line =~ s/([^\\])\.$/$1\?/g;   ##Convert . back to ?
+                       $line =~ s/\\\./\./g;           ##Convert \. to .
+                       $line =~ s/\.\*/\*/g;           ##Convert .* to *
+                   }
+                   $line =~ s/^([A-Z]):/$1:\t/g;
+                   print("$line\n");
+               }
+               print("\n");
+           }
     }
 
     if ($email && $email_git) {
@@ -377,6 +427,14 @@ if ($email) {
            }
        }
     }
+
+    foreach my $email (@file_emails) {
+       my ($name, $address) = parse_email($email);
+
+       my $tmp_email = format_email($name, $address, $email_usename);
+       push_email_address($tmp_email, '');
+       add_role($tmp_email, 'in file');
+    }
 }
 
 if ($email || $email_list) {
@@ -453,6 +511,7 @@ MAINTAINER field selection options:
     --remove-duplicates => minimize duplicate email names/addresses
     --roles => show roles (status:subsystem, git-signer, list, etc...)
     --rolestats => show roles and statistics (commits/total_commits, %)
+    --file-emails => add email addresses found in -f file (default: 0 (off))
   --scm => print SCM tree(s) if any
   --status => print status if any
   --subsystem => print subsystem name if any
@@ -466,6 +525,7 @@ Output type options:
 Other options:
   --pattern-depth => Number of pattern directory traversals (default: 0 (all))
   --keywords => scan patch for keywords (default: 1 (on))
+  --sections => print the entire subsystem sections with pattern matches
   --version => show version
   --help => show this help information
 
@@ -545,7 +605,7 @@ sub parse_email {
     $name =~ s/^\"|\"$//g;
     $address =~ s/^\s+|\s+$//g;
 
-    if ($name =~ /[^a-z0-9 \.\-]/i) {    ##has "must quote" chars
+    if ($name =~ /[^\w \-]/i) {         ##has "must quote" chars
        $name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
        $name = "\"$name\"";
     }
@@ -562,7 +622,7 @@ sub format_email {
     $name =~ s/^\"|\"$//g;
     $address =~ s/^\s+|\s+$//g;
 
-    if ($name =~ /[^a-z0-9 \.\-]/i) {    ##has "must quote" chars
+    if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
        $name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
        $name = "\"$name\"";
     }
@@ -811,7 +871,9 @@ sub add_role {
     foreach my $entry (@email_to) {
        if ($email_remove_duplicates) {
            my ($entry_name, $entry_address) = parse_email($entry->[0]);
-           if ($name eq $entry_name || $address eq $entry_address) {
+           if (($name eq $entry_name || $address eq $entry_address)
+               && ($role eq "" || !($entry->[1] =~ m/$role/))
+           ) {
                if ($entry->[1] eq "") {
                    $entry->[1] = "$role";
                } else {
@@ -819,7 +881,9 @@ sub add_role {
                }
            }
        } else {
-           if ($email eq $entry->[0]) {
+           if ($email eq $entry->[0]
+               && ($role eq "" || !($entry->[1] =~ m/$role/))
+           ) {
                if ($entry->[1] eq "") {
                    $entry->[1] = "$role";
                } else {
@@ -1099,6 +1163,51 @@ sub sort_and_uniq {
     return @parms;
 }
 
+sub clean_file_emails {
+    my (@file_emails) = @_;
+    my @fmt_emails = ();
+
+    foreach my $email (@file_emails) {
+       $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
+       my ($name, $address) = parse_email($email);
+       if ($name eq '"[,\.]"') {
+           $name = "";
+       }
+
+       my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
+       if (@nw > 2) {
+           my $first = $nw[@nw - 3];
+           my $middle = $nw[@nw - 2];
+           my $last = $nw[@nw - 1];
+
+           if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
+                (length($first) == 2 && substr($first, -1) eq ".")) ||
+               (length($middle) == 1 ||
+                (length($middle) == 2 && substr($middle, -1) eq "."))) {
+               $name = "$first $middle $last";
+           } else {
+               $name = "$middle $last";
+           }
+       }
+
+       if (substr($name, -1) =~ /[,\.]/) {
+           $name = substr($name, 0, length($name) - 1);
+       } elsif (substr($name, -2) =~ /[,\.]"/) {
+           $name = substr($name, 0, length($name) - 2) . '"';
+       }
+
+       if (substr($name, 0, 1) =~ /[,\.]/) {
+           $name = substr($name, 1, length($name) - 1);
+       } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
+           $name = '"' . substr($name, 2, length($name) - 2);
+       }
+
+       my $fmt_email = format_email($name, $address, $email_usename);
+       push(@fmt_emails, $fmt_email);
+    }
+    return @fmt_emails;
+}
+
 sub merge_email {
     my @lines;
     my %saw;
@@ -1183,7 +1292,7 @@ sub rfc822_strip_comments {
 
 #   valid: returns true if the parameter is an RFC822 valid address
 #
-sub rfc822_valid ($) {
+sub rfc822_valid {
     my $s = rfc822_strip_comments(shift);
 
     if (!$rfc822re) {
@@ -1203,7 +1312,7 @@ sub rfc822_valid ($) {
 #              from success with no addresses found, because an empty string is
 #              a valid list.
 
-sub rfc822_validlist ($) {
+sub rfc822_validlist {
     my $s = rfc822_strip_comments(shift);
 
     if (!$rfc822re) {
index a5721b373f53935601c63687acc5b4454acc54b4..5225e668dbf046fbe17ea105b23eb18cd11590d4 100644 (file)
@@ -387,7 +387,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
        struct smk_audit_info ad;
 
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
-       smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_mountpoint);
+       smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_root);
        smk_ad_setfield_u_fs_path_mnt(&ad, mnt);
 
        sbp = mnt->mnt_sb->s_security;
index c00df45c7ede899681344086e509c52f4b180bc0..cf7d61f781b9768cc8691cd3b4fc4c234a313b55 100644 (file)
@@ -88,29 +88,14 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname,
                sp = dentry->d_op->d_dname(dentry, newname + offset,
                                           newname_len - offset);
        } else {
-               /* Taken from d_namespace_path(). */
-               struct path root;
-               struct path ns_root = { };
-               struct path tmp;
+               struct path ns_root = {.mnt = NULL, .dentry = NULL};
 
-               read_lock(&current->fs->lock);
-               root = current->fs->root;
-               path_get(&root);
-               read_unlock(&current->fs->lock);
-               spin_lock(&vfsmount_lock);
-               if (root.mnt && root.mnt->mnt_ns)
-                       ns_root.mnt = mntget(root.mnt->mnt_ns->root);
-               if (ns_root.mnt)
-                       ns_root.dentry = dget(ns_root.mnt->mnt_root);
-               spin_unlock(&vfsmount_lock);
                spin_lock(&dcache_lock);
-               tmp = ns_root;
-               sp = __d_path(path, &tmp, newname, newname_len);
+               /* go to whatever namespace root we are under */
+               sp = __d_path(path, &ns_root, newname, newname_len);
                spin_unlock(&dcache_lock);
-               path_put(&root);
-               path_put(&ns_root);
                /* Prepend "/proc" prefix if using internal proc vfs mount. */
-               if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) &&
+               if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
                    (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
                        sp -= 5;
                        if (sp >= newname)
index a2763c2e7348cea2422b8b0f88f86a3002e40da2..9cd0a66b7663a8d774201ca276fbde0c6494f758 100644 (file)
@@ -137,7 +137,7 @@ static void uda1380_flush_work(struct work_struct *work)
 {
        int bit, reg;
 
-       for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
+       for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
                reg = 0x10 + bit;
                pr_debug("uda1380: flush reg %x val %x:\n", reg,
                                uda1380_read_reg_cache(uda1380_codec, reg));
index 9cc04ab2bce75cd520b56283e30798d16870dcbc..c0bfab8fed3d8258730b9577345f8c979740dec2 100644 (file)
@@ -72,7 +72,7 @@ struct siu_firmware {
 #include <linux/interrupt.h>
 #include <linux/io.h>
 
-#include <asm/dma-sh.h>
+#include <asm/dmaengine.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
index c5efc30f0136ca202506a2130e0375a95dc4a5bc..ba7f8d05d9775b8427381220dc33a3f3292e026e 100644 (file)
@@ -32,7 +32,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc-dai.h>
 
-#include <asm/dma-sh.h>
+#include <asm/dmaengine.h>
 #include <asm/siu.h>
 
 #include "siu.h"
index 2de34075f6a49dd443929bc9408f8887fb66812b..34202b1be0bb8ffd149755b4c3091922fc77dac5 100644 (file)
@@ -41,7 +41,8 @@ OPTIONS
 
 -d::
 --del=::
-       Delete a probe event.
+       Delete probe events. This accepts glob wildcards('*', '?') and character
+       classes(e.g. [a-z], [!A-Z]).
 
 -l::
 --list::
@@ -50,17 +51,29 @@ OPTIONS
 -L::
 --line=::
        Show source code lines which can be probed. This needs an argument
-       which specifies a range of the source code.
+       which specifies a range of the source code. (see LINE SYNTAX for detail)
+
+-f::
+--force::
+       Forcibly add events with existing name.
 
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
 
- "[EVENT=]FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]"
+    1) Define event based on function name
+     [EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
+
+    2) Define event based on source file with line number
+     [EVENT=]SRC:ALN [ARG ...]
+
+    3) Define event based on source file with lazy pattern
+     [EVENT=]SRC;PTN [ARG ...]
+
 
 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
-'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, 'RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. In addition, 'SRC' specifies a source file which has that function.
-It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number.
+'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition.  In addition, '@SRC' specifies a source file which has that function.
+It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
 
 LINE SYNTAX
@@ -76,6 +89,41 @@ and 'ALN2' is end line number in the file. It is also possible to specify how
 many lines to show by using 'NUM'.
 So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
 
+LAZY MATCHING
+-------------
+ The lazy line matching is similar to glob matching but ignoring spaces in both of pattern and target. So this accepts wildcards('*', '?') and character classes(e.g. [a-z], [!A-Z]).
+
+e.g.
+ 'a=*' can matches 'a=b', 'a = b', 'a == b' and so on.
+
+This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.)
+
+
+EXAMPLES
+--------
+Display which lines in schedule() can be probed:
+
+ ./perf probe --line schedule
+
+Add a probe on schedule() function 12th line with recording cpu local variable:
+
+ ./perf probe schedule:12 cpu
+ or
+ ./perf probe --add='schedule:12 cpu'
+
+ this will add one or more probes which has the name start with "schedule".
+
+ Add probes on lines in schedule() function which calls update_rq_clock().
+
+ ./perf probe 'schedule;update_rq_clock*'
+ or
+ ./perf probe --add='schedule;update_rq_clock*'
+
+Delete all probes on schedule().
+
+ ./perf probe --del='schedule*'
+
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1]
index 54a5b50ff312c70efe7f1e66cc84f8f2dce9cf55..2d537382c68602fa72e4b9dec7f30c20a208aec2 100644 (file)
@@ -500,12 +500,12 @@ else
        msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]);
 endif
 
-ifneq ($(shell sh -c "(echo '\#ifndef _MIPS_SZLONG'; echo '\#define _MIPS_SZLONG 0'; echo '\#endif'; echo '\#include <dwarf.h>'; echo '\#include <libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/libdwarf -ldwarf -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
-       msg := $(warning No libdwarf.h found or old libdwarf.h found, disables dwarf support. Please install libdwarf-dev/libdwarf-devel >= 20081231);
-       BASIC_CFLAGS += -DNO_LIBDWARF
+ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
+       msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev);
+       BASIC_CFLAGS += -DNO_DWARF_SUPPORT
 else
-       BASIC_CFLAGS += -I/usr/include/libdwarf
-       EXTLIBS += -lelf -ldwarf
+       BASIC_CFLAGS += -I/usr/include/elfutils
+       EXTLIBS += -lelf -ldw
        LIB_OBJS += util/probe-finder.o
 endif
 
index ad47bd4c50efa5354aed01e5ff081836028c7638..c30a33592340e4977c4d82cfb7dabd618370be2c 100644 (file)
@@ -128,7 +128,7 @@ static void evaluate_probe_point(struct probe_point *pp)
                    pp->function);
 }
 
-#ifndef NO_LIBDWARF
+#ifndef NO_DWARF_SUPPORT
 static int open_vmlinux(void)
 {
        if (map__load(session.kmaps[MAP__FUNCTION], NULL) < 0) {
@@ -156,14 +156,16 @@ static const char * const probe_usage[] = {
        "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
        "perf probe [<options>] --del '[GROUP:]EVENT' ...",
        "perf probe --list",
+#ifndef NO_DWARF_SUPPORT
        "perf probe --line 'LINEDESC'",
+#endif
        NULL
 };
 
 static const struct option options[] = {
        OPT_BOOLEAN('v', "verbose", &verbose,
                    "be more verbose (show parsed arguments, etc)"),
-#ifndef NO_LIBDWARF
+#ifndef NO_DWARF_SUPPORT
        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
                   "file", "vmlinux pathname"),
 #endif
@@ -172,30 +174,32 @@ static const struct option options[] = {
        OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
                opt_del_probe_event),
        OPT_CALLBACK('a', "add", NULL,
-#ifdef NO_LIBDWARF
-               "[EVENT=]FUNC[+OFFS|%return] [ARG ...]",
+#ifdef NO_DWARF_SUPPORT
+               "[EVENT=]FUNC[+OFF|%return] [ARG ...]",
 #else
-               "[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]",
+               "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
+               " [ARG ...]",
 #endif
                "probe point definition, where\n"
                "\t\tGROUP:\tGroup name (optional)\n"
                "\t\tEVENT:\tEvent name\n"
                "\t\tFUNC:\tFunction name\n"
-               "\t\tOFFS:\tOffset from function entry (in byte)\n"
+               "\t\tOFF:\tOffset from function entry (in byte)\n"
                "\t\t%return:\tPut the probe at function return\n"
-#ifdef NO_LIBDWARF
+#ifdef NO_DWARF_SUPPORT
                "\t\tARG:\tProbe argument (only \n"
 #else
                "\t\tSRC:\tSource code path\n"
-               "\t\tRLN:\tRelative line number from function entry.\n"
-               "\t\tALN:\tAbsolute line number in file.\n"
+               "\t\tRL:\tRelative line number from function entry.\n"
+               "\t\tAL:\tAbsolute line number in file.\n"
+               "\t\tPT:\tLazy expression of line code.\n"
                "\t\tARG:\tProbe argument (local variable name or\n"
 #endif
                "\t\t\tkprobe-tracer argument format.)\n",
                opt_add_probe_event),
        OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
                    " with existing name"),
-#ifndef NO_LIBDWARF
+#ifndef NO_DWARF_SUPPORT
        OPT_CALLBACK('L', "line", NULL,
                     "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
                     "Show source code lines.", opt_show_lines),
@@ -223,7 +227,7 @@ static void init_vmlinux(void)
 int cmd_probe(int argc, const char **argv, const char *prefix __used)
 {
        int i, ret;
-#ifndef NO_LIBDWARF
+#ifndef NO_DWARF_SUPPORT
        int fd;
 #endif
        struct probe_point *pp;
@@ -259,7 +263,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
                return 0;
        }
 
-#ifndef NO_LIBDWARF
+#ifndef NO_DWARF_SUPPORT
        if (session.show_lines) {
                if (session.nr_probe != 0 || session.dellist) {
                        pr_warning("  Error: Don't use --line with"
@@ -290,9 +294,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
        init_vmlinux();
 
        if (session.need_dwarf)
-#ifdef NO_LIBDWARF
+#ifdef NO_DWARF_SUPPORT
                die("Debuginfo-analysis is not supported");
-#else  /* !NO_LIBDWARF */
+#else  /* !NO_DWARF_SUPPORT */
                pr_debug("Some probes require debuginfo.\n");
 
        fd = open_vmlinux();
@@ -312,7 +316,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
                        continue;
 
                lseek(fd, SEEK_SET, 0);
-               ret = find_probepoint(fd, pp);
+               ret = find_probe_point(fd, pp);
                if (ret > 0)
                        continue;
                if (ret == 0) { /* No error but failed to find probe point. */
@@ -333,7 +337,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
        close(fd);
 
 end_dwarf:
-#endif /* !NO_LIBDWARF */
+#endif /* !NO_DWARF_SUPPORT */
 
        /* Synthesize probes without dwarf */
        for (i = 0; i < session.nr_probe; i++) {
index 8f05688496919d6e46dcf55b60856d8896ef9b24..c971e81e9cbf2e9276780a81fe7915bd478c29c2 100644 (file)
@@ -119,14 +119,14 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
        char c, nc = 0;
        /*
         * <Syntax>
-        * perf probe [EVENT=]SRC:LN
-        * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC]
+        * perf probe [EVENT=]SRC[:LN|;PTN]
+        * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
         *
         * TODO:Group name support
         */
 
-       ptr = strchr(arg, '=');
-       if (ptr) {      /* Event name */
+       ptr = strpbrk(arg, ";=@+%");
+       if (ptr && *ptr == '=') {       /* Event name */
                *ptr = '\0';
                tmp = ptr + 1;
                ptr = strchr(arg, ':');
@@ -139,7 +139,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
                arg = tmp;
        }
 
-       ptr = strpbrk(arg, ":+@%");
+       ptr = strpbrk(arg, ";:+@%");
        if (ptr) {
                nc = *ptr;
                *ptr++ = '\0';
@@ -156,7 +156,11 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
        while (ptr) {
                arg = ptr;
                c = nc;
-               ptr = strpbrk(arg, ":+@%");
+               if (c == ';') { /* Lazy pattern must be the last part */
+                       pp->lazy_line = strdup(arg);
+                       break;
+               }
+               ptr = strpbrk(arg, ";:+@%");
                if (ptr) {
                        nc = *ptr;
                        *ptr++ = '\0';
@@ -165,13 +169,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
                case ':':       /* Line number */
                        pp->line = strtoul(arg, &tmp, 0);
                        if (*tmp != '\0')
-                               semantic_error("There is non-digit charactor"
-                                               " in line number.");
+                               semantic_error("There is non-digit char"
+                                              " in line number.");
                        break;
                case '+':       /* Byte offset from a symbol */
                        pp->offset = strtoul(arg, &tmp, 0);
                        if (*tmp != '\0')
-                               semantic_error("There is non-digit charactor"
+                               semantic_error("There is non-digit character"
                                                " in offset.");
                        break;
                case '@':       /* File name */
@@ -179,9 +183,6 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
                                semantic_error("SRC@SRC is not allowed.");
                        pp->file = strdup(arg);
                        DIE_IF(pp->file == NULL);
-                       if (ptr)
-                               semantic_error("@SRC must be the last "
-                                              "option.");
                        break;
                case '%':       /* Probe places */
                        if (strcmp(arg, "return") == 0) {
@@ -196,11 +197,18 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
        }
 
        /* Exclusion check */
+       if (pp->lazy_line && pp->line)
+               semantic_error("Lazy pattern can't be used with line number.");
+
+       if (pp->lazy_line && pp->offset)
+               semantic_error("Lazy pattern can't be used with offset.");
+
        if (pp->line && pp->offset)
                semantic_error("Offset can't be used with line number.");
 
-       if (!pp->line && pp->file && !pp->function)
-               semantic_error("File always requires line number.");
+       if (!pp->line && !pp->lazy_line && pp->file && !pp->function)
+               semantic_error("File always requires line number or "
+                              "lazy pattern.");
 
        if (pp->offset && !pp->function)
                semantic_error("Offset requires an entry function.");
@@ -208,11 +216,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
        if (pp->retprobe && !pp->function)
                semantic_error("Return probe requires an entry function.");
 
-       if ((pp->offset || pp->line) && pp->retprobe)
-               semantic_error("Offset/Line can't be used with return probe.");
+       if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe)
+               semantic_error("Offset/Line/Lazy pattern can't be used with "
+                              "return probe.");
 
-       pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n",
-                pp->function, pp->file, pp->line, pp->offset, pp->retprobe);
+       pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n",
+                pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
+                pp->lazy_line);
 }
 
 /* Parse perf-probe event definition */
@@ -458,6 +468,8 @@ static void clear_probe_point(struct probe_point *pp)
                free(pp->function);
        if (pp->file)
                free(pp->file);
+       if (pp->lazy_line)
+               free(pp->lazy_line);
        for (i = 0; i < pp->nr_args; i++)
                free(pp->args[i]);
        if (pp->args)
@@ -719,6 +731,7 @@ void del_trace_kprobe_events(struct strlist *dellist)
 }
 
 #define LINEBUF_SIZE 256
+#define NR_ADDITIONAL_LINES 2
 
 static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
 {
@@ -779,5 +792,11 @@ void show_line_range(struct line_range *lr)
                        show_one_line(fp, (l++) - lr->offset, false, false);
                show_one_line(fp, (l++) - lr->offset, false, true);
        }
+
+       if (lr->end == INT_MAX)
+               lr->end = l + NR_ADDITIONAL_LINES;
+       while (l < lr->end && !feof(fp))
+               show_one_line(fp, (l++) - lr->offset, false, false);
+
        fclose(fp);
 }
index 1b2124d12f6831fad08e9a0a0edddd148a4576c2..e77dc886760e5350ea56d1043d400e914678b817 100644 (file)
 #include <stdarg.h>
 #include <ctype.h>
 
+#include "string.h"
 #include "event.h"
 #include "debug.h"
 #include "util.h"
 #include "probe-finder.h"
 
 
-/* Dwarf_Die Linkage to parent Die */
-struct die_link {
-       struct die_link *parent;        /* Parent die */
-       Dwarf_Die die;                  /* Current die */
-};
-
-static Dwarf_Debug __dw_debug;
-static Dwarf_Error __dw_error;
-
 /*
  * Generic dwarf analysis helpers
  */
@@ -113,281 +105,190 @@ static int strtailcmp(const char *s1, const char *s2)
        return 0;
 }
 
-/* Find the fileno of the target file. */
-static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
-{
-       Dwarf_Signed cnt, i;
-       Dwarf_Unsigned found = 0;
-       char **srcs;
-       int ret;
+/* Line number list operations */
 
-       if (!fname)
-               return 0;
+/* Add a line to line number list */
+static void line_list__add_line(struct list_head *head, unsigned int line)
+{
+       struct line_node *ln;
+       struct list_head *p;
 
-       ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
-       if (ret == DW_DLV_OK) {
-               for (i = 0; i < cnt && !found; i++) {
-                       if (strtailcmp(srcs[i], fname) == 0)
-                               found = i + 1;
-                       dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
-               }
-               for (; i < cnt; i++)
-                       dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
-               dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
+       /* Reverse search, because new line will be the last one */
+       list_for_each_entry_reverse(ln, head, list) {
+               if (ln->line < line) {
+                       p = &ln->list;
+                       goto found;
+               } else if (ln->line == line)    /* Already exist */
+                       return ;
        }
-       if (found)
-               pr_debug("found fno: %d\n", (int)found);
-       return found;
+       /* List is empty, or the smallest entry */
+       p = head;
+found:
+       pr_debug("line list: add a line %u\n", line);
+       ln = zalloc(sizeof(struct line_node));
+       DIE_IF(ln == NULL);
+       ln->line = line;
+       INIT_LIST_HEAD(&ln->list);
+       list_add(&ln->list, p);
 }
 
-static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf)
+/* Check if the line in line number list */
+static int line_list__has_line(struct list_head *head, unsigned int line)
 {
-       Dwarf_Signed cnt, i;
-       char **srcs;
-       int ret = 0;
-
-       if (!buf || !fno)
-               return -EINVAL;
-
-       ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
-       if (ret == DW_DLV_OK) {
-               if ((Dwarf_Unsigned)cnt > fno - 1) {
-                       *buf = strdup(srcs[fno - 1]);
-                       ret = 0;
-                       pr_debug("found filename: %s\n", *buf);
-               } else
-                       ret = -ENOENT;
-               for (i = 0; i < cnt; i++)
-                       dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
-               dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
-       } else
-               ret = -EINVAL;
-       return ret;
+       struct line_node *ln;
+
+       /* Reverse search, because new line will be the last one */
+       list_for_each_entry(ln, head, list)
+               if (ln->line == line)
+                       return 1;
+
+       return 0;
 }
 
-/* Compare diename and tname */
-static int die_compare_name(Dwarf_Die dw_die, const char *tname)
+/* Init line number list */
+static void line_list__init(struct list_head *head)
 {
-       char *name;
-       int ret;
-       ret = dwarf_diename(dw_die, &name, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (ret == DW_DLV_OK) {
-               ret = strcmp(tname, name);
-               dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
-       } else
-               ret = -1;
-       return ret;
+       INIT_LIST_HEAD(head);
 }
 
-/* Check the address is in the subprogram(function). */
-static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
-                                Dwarf_Signed *offs)
+/* Free line number list */
+static void line_list__free(struct list_head *head)
 {
-       Dwarf_Addr lopc, hipc;
-       int ret;
-
-       /* TODO: check ranges */
-       ret = dwarf_lowpc(sp_die, &lopc, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (ret == DW_DLV_NO_ENTRY)
-               return 0;
-       ret = dwarf_highpc(sp_die, &hipc, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       if (lopc <= addr && addr < hipc) {
-               *offs = addr - lopc;
-               return 1;
-       } else
-               return 0;
+       struct line_node *ln;
+       while (!list_empty(head)) {
+               ln = list_first_entry(head, struct line_node, list);
+               list_del(&ln->list);
+               free(ln);
+       }
 }
 
-/* Check the die is inlined function */
-static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die)
+/* Dwarf wrappers */
+
+/* Find the realpath of the target file. */
+static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
 {
-       /* TODO: check strictly */
-       Dwarf_Bool inl;
+       Dwarf_Files *files;
+       size_t nfiles, i;
+       const char *src;
        int ret;
 
-       ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       return inl;
-}
+       if (!fname)
+               return NULL;
 
-/* Get the offset of abstruct_origin */
-static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die)
-{
-       Dwarf_Attribute attr;
-       Dwarf_Off cu_offs;
-       int ret;
+       ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
+       if (ret != 0)
+               return NULL;
 
-       ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       ret = dwarf_formref(attr, &cu_offs, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
-       return cu_offs;
+       for (i = 0; i < nfiles; i++) {
+               src = dwarf_filesrc(files, i, NULL, NULL);
+               if (strtailcmp(src, fname) == 0)
+                       break;
+       }
+       return src;
 }
 
-/* Get entry pc(or low pc, 1st entry of ranges)  of the die */
-static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die)
+struct __addr_die_search_param {
+       Dwarf_Addr      addr;
+       Dwarf_Die       *die_mem;
+};
+
+static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
 {
-       Dwarf_Attribute attr;
-       Dwarf_Addr addr;
-       Dwarf_Off offs;
-       Dwarf_Ranges *ranges;
-       Dwarf_Signed cnt;
-       int ret;
+       struct __addr_die_search_param *ad = data;
 
-       /* Try to get entry pc */
-       ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (ret == DW_DLV_OK) {
-               ret = dwarf_formaddr(attr, &addr, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
-               dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
-               return addr;
+       if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
+           dwarf_haspc(fn_die, ad->addr)) {
+               memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
+               return DWARF_CB_ABORT;
        }
+       return DWARF_CB_OK;
+}
 
-       /* Try to get low pc */
-       ret = dwarf_lowpc(dw_die, &addr, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (ret == DW_DLV_OK)
-               return addr;
-
-       /* Try to get ranges */
-       ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       ret = dwarf_formref(attr, &offs, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL,
-                               &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       addr = ranges[0].dwr_addr1;
-       dwarf_ranges_dealloc(__dw_debug, ranges, cnt);
-       return addr;
+/* Search a real subprogram including this line, */
+static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr,
+                                         Dwarf_Die *die_mem)
+{
+       struct __addr_die_search_param ad;
+       ad.addr = addr;
+       ad.die_mem = die_mem;
+       /* dwarf_getscopes can't find subprogram. */
+       if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
+               return NULL;
+       else
+               return die_mem;
 }
 
-/*
- * Search a Die from Die tree.
- * Note: cur_link->die should be deallocated in this function.
- */
-static int __search_die_tree(struct die_link *cur_link,
-                            int (*die_cb)(struct die_link *, void *),
-                            void *data)
+/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
+static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
+                                    Dwarf_Die *die_mem)
 {
-       Dwarf_Die new_die;
-       struct die_link new_link;
+       Dwarf_Die child_die;
        int ret;
 
-       if (!die_cb)
-               return 0;
-
-       /* Check current die */
-       while (!(ret = die_cb(cur_link, data))) {
-               /* Check child die */
-               ret = dwarf_child(cur_link->die, &new_die, &__dw_error);
-               DIE_IF(ret == DW_DLV_ERROR);
-               if (ret == DW_DLV_OK) {
-                       new_link.parent = cur_link;
-                       new_link.die = new_die;
-                       ret = __search_die_tree(&new_link, die_cb, data);
-                       if (ret)
-                               break;
-               }
+       ret = dwarf_child(sp_die, die_mem);
+       if (ret != 0)
+               return NULL;
 
-               /* Move to next sibling */
-               ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die,
-                                     &__dw_error);
-               DIE_IF(ret == DW_DLV_ERROR);
-               dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
-               cur_link->die = new_die;
-               if (ret == DW_DLV_NO_ENTRY)
-                       return 0;
-       }
-       dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
-       return ret;
-}
+       do {
+               if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
+                   dwarf_haspc(die_mem, addr))
+                       return die_mem;
 
-/* Search a die in its children's die tree */
-static int search_die_from_children(Dwarf_Die parent_die,
-                                   int (*die_cb)(struct die_link *, void *),
-                                   void *data)
-{
-       struct die_link new_link;
-       int ret;
+               if (die_get_inlinefunc(die_mem, addr, &child_die)) {
+                       memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
+                       return die_mem;
+               }
+       } while (dwarf_siblingof(die_mem, die_mem) == 0);
 
-       new_link.parent = NULL;
-       ret = dwarf_child(parent_die, &new_link.die, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (ret == DW_DLV_OK)
-               return __search_die_tree(&new_link, die_cb, data);
-       else
-               return 0;
+       return NULL;
 }
 
-/* Find a locdesc corresponding to the address */
-static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
-                           Dwarf_Addr addr)
+/* Compare diename and tname */
+static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
 {
-       Dwarf_Signed lcnt;
-       Dwarf_Locdesc **llbuf;
-       int ret, i;
-
-       ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       ret = DW_DLV_NO_ENTRY;
-       for (i = 0; i < lcnt; ++i) {
-               if (llbuf[i]->ld_lopc <= addr &&
-                   llbuf[i]->ld_hipc > addr) {
-                       memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
-                       desc->ld_s =
-                               malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
-                       DIE_IF(desc->ld_s == NULL);
-                       memcpy(desc->ld_s, llbuf[i]->ld_s,
-                               sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
-                       ret = DW_DLV_OK;
-                       break;
-               }
-               dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
-               dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
-       }
-       /* Releasing loop */
-       for (; i < lcnt; ++i) {
-               dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
-               dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
-       }
-       dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST);
-       return ret;
+       const char *name;
+       name = dwarf_diename(dw_die);
+       DIE_IF(name == NULL);
+       return strcmp(tname, name);
 }
 
-/* Get decl_file attribute value (file number) */
-static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die)
+/* Get entry pc(or low pc, 1st entry of ranges)  of the die */
+static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
 {
-       Dwarf_Attribute attr;
-       Dwarf_Unsigned fno;
+       Dwarf_Addr epc;
        int ret;
 
-       ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       dwarf_formudata(attr, &fno, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
-       return fno;
+       ret = dwarf_entrypc(dw_die, &epc);
+       DIE_IF(ret == -1);
+       return epc;
 }
 
-/* Get decl_line attribute value (line number) */
-static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die)
+/* Get a variable die */
+static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
+                                   Dwarf_Die *die_mem)
 {
-       Dwarf_Attribute attr;
-       Dwarf_Unsigned lno;
+       Dwarf_Die child_die;
+       int tag;
        int ret;
 
-       ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       dwarf_formudata(attr, &lno, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
-       return lno;
+       ret = dwarf_child(sp_die, die_mem);
+       if (ret != 0)
+               return NULL;
+
+       do {
+               tag = dwarf_tag(die_mem);
+               if ((tag == DW_TAG_formal_parameter ||
+                    tag == DW_TAG_variable) &&
+                   (die_compare_name(die_mem, name) == 0))
+                       return die_mem;
+
+               if (die_find_variable(die_mem, name, &child_die)) {
+                       memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
+                       return die_mem;
+               }
+       } while (dwarf_siblingof(die_mem, die_mem) == 0);
+
+       return NULL;
 }
 
 /*
@@ -395,47 +296,45 @@ static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die)
  */
 
 /* Show a location */
-static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
+static void show_location(Dwarf_Op *op, struct probe_finder *pf)
 {
-       Dwarf_Small op;
-       Dwarf_Unsigned regn;
-       Dwarf_Signed offs;
+       unsigned int regn;
+       Dwarf_Word offs = 0;
        int deref = 0, ret;
        const char *regs;
 
-       op = loc->lr_atom;
-
+       /* TODO: support CFA */
        /* If this is based on frame buffer, set the offset */
-       if (op == DW_OP_fbreg) {
+       if (op->atom == DW_OP_fbreg) {
+               if (pf->fb_ops == NULL)
+                       die("The attribute of frame base is not supported.\n");
                deref = 1;
-               offs = (Dwarf_Signed)loc->lr_number;
-               op = pf->fbloc.ld_s[0].lr_atom;
-               loc = &pf->fbloc.ld_s[0];
-       } else
-               offs = 0;
+               offs = op->number;
+               op = &pf->fb_ops[0];
+       }
 
-       if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
-               regn = op - DW_OP_breg0;
-               offs += (Dwarf_Signed)loc->lr_number;
+       if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
+               regn = op->atom - DW_OP_breg0;
+               offs += op->number;
                deref = 1;
-       } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
-               regn = op - DW_OP_reg0;
-       } else if (op == DW_OP_bregx) {
-               regn = loc->lr_number;
-               offs += (Dwarf_Signed)loc->lr_number2;
+       } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) {
+               regn = op->atom - DW_OP_reg0;
+       } else if (op->atom == DW_OP_bregx) {
+               regn = op->number;
+               offs += op->number2;
                deref = 1;
-       } else if (op == DW_OP_regx) {
-               regn = loc->lr_number;
+       } else if (op->atom == DW_OP_regx) {
+               regn = op->number;
        } else
-               die("Dwarf_OP %d is not supported.", op);
+               die("DW_OP %d is not supported.", op->atom);
 
        regs = get_arch_regstr(regn);
        if (!regs)
-               die("%lld exceeds max register number.", regn);
+               die("%u exceeds max register number.", regn);
 
        if (deref)
-               ret = snprintf(pf->buf, pf->len,
-                                " %s=%+lld(%s)", pf->var, offs, regs);
+               ret = snprintf(pf->buf, pf->len, " %s=+%ju(%s)",
+                              pf->var, (uintmax_t)offs, regs);
        else
                ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs);
        DIE_IF(ret < 0);
@@ -443,52 +342,37 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
 }
 
 /* Show a variables in kprobe event format */
-static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
+static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
 {
        Dwarf_Attribute attr;
-       Dwarf_Locdesc ld;
+       Dwarf_Op *expr;
+       size_t nexpr;
        int ret;
 
-       ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error);
-       if (ret != DW_DLV_OK)
+       if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
                goto error;
-       ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
-       if (ret != DW_DLV_OK)
+       /* TODO: handle more than 1 exprs */
+       ret = dwarf_getlocation_addr(&attr, (pf->addr - pf->cu_base),
+                                    &expr, &nexpr, 1);
+       if (ret <= 0 || nexpr == 0)
                goto error;
-       /* TODO? */
-       DIE_IF(ld.ld_cents != 1);
-       show_location(&ld.ld_s[0], pf);
-       free(ld.ld_s);
-       dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
+
+       show_location(expr, pf);
+       /* *expr will be cached in libdw. Don't free it. */
        return ;
 error:
+       /* TODO: Support const_value */
        die("Failed to find the location of %s at this address.\n"
            " Perhaps, it has been optimized out.", pf->var);
 }
 
-static int variable_callback(struct die_link *dlink, void *data)
-{
-       struct probe_finder *pf = (struct probe_finder *)data;
-       Dwarf_Half tag;
-       int ret;
-
-       ret = dwarf_tag(dlink->die, &tag, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if ((tag == DW_TAG_formal_parameter ||
-            tag == DW_TAG_variable) &&
-           (die_compare_name(dlink->die, pf->var) == 0)) {
-               show_variable(dlink->die, pf);
-               return 1;
-       }
-       /* TODO: Support struct members and arrays */
-       return 0;
-}
-
 /* Find a variable in a subprogram die */
-static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
+static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
 {
        int ret;
+       Dwarf_Die vr_die;
 
+       /* TODO: Support struct members and arrays */
        if (!is_c_varname(pf->var)) {
                /* Output raw parameters */
                ret = snprintf(pf->buf, pf->len, " %s", pf->var);
@@ -499,58 +383,51 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
 
        pr_debug("Searching '%s' variable in context.\n", pf->var);
        /* Search child die for local variables and parameters. */
-       ret = search_die_from_children(sp_die, variable_callback, pf);
-       if (!ret)
+       if (!die_find_variable(sp_die, pf->var, &vr_die))
                die("Failed to find '%s' in this function.", pf->var);
-}
-
-/* Get a frame base on the address */
-static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
-{
-       Dwarf_Attribute attr;
-       int ret;
 
-       ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
-       ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
-       DIE_IF(ret != DW_DLV_OK);
-       dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
-}
-
-static void free_current_frame_base(struct probe_finder *pf)
-{
-       free(pf->fbloc.ld_s);
-       memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
+       show_variable(&vr_die, pf);
 }
 
 /* Show a probe point to output buffer */
-static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
-                           struct probe_finder *pf)
+static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
 {
        struct probe_point *pp = pf->pp;
-       char *name;
+       Dwarf_Addr eaddr;
+       Dwarf_Die die_mem;
+       const char *name;
        char tmp[MAX_PROBE_BUFFER];
        int ret, i, len;
+       Dwarf_Attribute fb_attr;
+       size_t nops;
+
+       /* If no real subprogram, find a real one */
+       if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
+               sp_die = die_get_real_subprogram(&pf->cu_die,
+                                                pf->addr, &die_mem);
+               if (!sp_die)
+                       die("Probe point is not found in subprograms.");
+       }
 
        /* Output name of probe point */
-       ret = dwarf_diename(sp_die, &name, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (ret == DW_DLV_OK) {
-               ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
-                               (unsigned int)offs);
+       name = dwarf_diename(sp_die);
+       if (name) {
+               dwarf_entrypc(sp_die, &eaddr);
+               ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name,
+                               (unsigned long)(pf->addr - eaddr));
                /* Copy the function name if possible */
                if (!pp->function) {
                        pp->function = strdup(name);
-                       pp->offset = offs;
+                       pp->offset = (size_t)(pf->addr - eaddr);
                }
-               dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
        } else {
                /* This function has no name. */
-               ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr);
+               ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx",
+                              (uintmax_t)pf->addr);
                if (!pp->function) {
                        /* TODO: Use _stext */
                        pp->function = strdup("");
-                       pp->offset = (int)pf->addr;
+                       pp->offset = (size_t)pf->addr;
                }
        }
        DIE_IF(ret < 0);
@@ -558,8 +435,15 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
        len = ret;
        pr_debug("Probe point found: %s\n", tmp);
 
+       /* Get the frame base attribute/ops */
+       dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
+       ret = dwarf_getlocation_addr(&fb_attr, (pf->addr - pf->cu_base),
+                                    &pf->fb_ops, &nops, 1);
+       if (ret <= 0 || nops == 0)
+               pf->fb_ops = NULL;
+
        /* Find each argument */
-       get_current_frame_base(sp_die, pf);
+       /* TODO: use dwarf_cfi_addrframe */
        for (i = 0; i < pp->nr_args; i++) {
                pf->var = pp->args[i];
                pf->buf = &tmp[len];
@@ -567,289 +451,327 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
                find_variable(sp_die, pf);
                len += strlen(pf->buf);
        }
-       free_current_frame_base(pf);
+
+       /* *pf->fb_ops will be cached in libdw. Don't free it. */
+       pf->fb_ops = NULL;
 
        pp->probes[pp->found] = strdup(tmp);
        pp->found++;
 }
 
-static int probeaddr_callback(struct die_link *dlink, void *data)
+/* Find probe point from its line number */
+static void find_probe_point_by_line(struct probe_finder *pf)
 {
-       struct probe_finder *pf = (struct probe_finder *)data;
-       Dwarf_Half tag;
-       Dwarf_Signed offs;
+       Dwarf_Lines *lines;
+       Dwarf_Line *line;
+       size_t nlines, i;
+       Dwarf_Addr addr;
+       int lineno;
        int ret;
 
-       ret = dwarf_tag(dlink->die, &tag, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       /* Check the address is in this subprogram */
-       if (tag == DW_TAG_subprogram &&
-           die_within_subprogram(dlink->die, pf->addr, &offs)) {
-               show_probepoint(dlink->die, offs, pf);
-               return 1;
+       ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
+       DIE_IF(ret != 0);
+
+       for (i = 0; i < nlines; i++) {
+               line = dwarf_onesrcline(lines, i);
+               dwarf_lineno(line, &lineno);
+               if (lineno != pf->lno)
+                       continue;
+
+               /* TODO: Get fileno from line, but how? */
+               if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
+                       continue;
+
+               ret = dwarf_lineaddr(line, &addr);
+               DIE_IF(ret != 0);
+               pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
+                        (int)i, lineno, (uintmax_t)addr);
+               pf->addr = addr;
+
+               show_probe_point(NULL, pf);
+               /* Continuing, because target line might be inlined. */
        }
-       return 0;
 }
 
-/* Find probe point from its line number */
-static void find_probe_point_by_line(struct probe_finder *pf)
+/* Find lines which match lazy pattern */
+static int find_lazy_match_lines(struct list_head *head,
+                                const char *fname, const char *pat)
 {
-       Dwarf_Signed cnt, i, clm;
-       Dwarf_Line *lines;
-       Dwarf_Unsigned lineno = 0;
+       char *fbuf, *p1, *p2;
+       int fd, line, nlines = 0;
+       struct stat st;
+
+       fd = open(fname, O_RDONLY);
+       if (fd < 0)
+               die("failed to open %s", fname);
+       DIE_IF(fstat(fd, &st) < 0);
+       fbuf = malloc(st.st_size + 2);
+       DIE_IF(fbuf == NULL);
+       DIE_IF(read(fd, fbuf, st.st_size) < 0);
+       close(fd);
+       fbuf[st.st_size] = '\n';        /* Dummy line */
+       fbuf[st.st_size + 1] = '\0';
+       p1 = fbuf;
+       line = 1;
+       while ((p2 = strchr(p1, '\n')) != NULL) {
+               *p2 = '\0';
+               if (strlazymatch(p1, pat)) {
+                       line_list__add_line(head, line);
+                       nlines++;
+               }
+               line++;
+               p1 = p2 + 1;
+       }
+       free(fbuf);
+       return nlines;
+}
+
+/* Find probe points from lazy pattern  */
+static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+       Dwarf_Lines *lines;
+       Dwarf_Line *line;
+       size_t nlines, i;
        Dwarf_Addr addr;
-       Dwarf_Unsigned fno;
+       Dwarf_Die die_mem;
+       int lineno;
        int ret;
 
-       ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
+       if (list_empty(&pf->lcache)) {
+               /* Matching lazy line pattern */
+               ret = find_lazy_match_lines(&pf->lcache, pf->fname,
+                                           pf->pp->lazy_line);
+               if (ret <= 0)
+                       die("No matched lines found in %s.", pf->fname);
+       }
+
+       ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
+       DIE_IF(ret != 0);
+       for (i = 0; i < nlines; i++) {
+               line = dwarf_onesrcline(lines, i);
 
-       for (i = 0; i < cnt; i++) {
-               ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
-               if (fno != pf->fno)
+               dwarf_lineno(line, &lineno);
+               if (!line_list__has_line(&pf->lcache, lineno))
                        continue;
 
-               ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
-               if (lineno != pf->lno)
+               /* TODO: Get fileno from line, but how? */
+               if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
                        continue;
 
-               ret = dwarf_lineoff(lines[i], &clm, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
+               ret = dwarf_lineaddr(line, &addr);
+               DIE_IF(ret != 0);
+               if (sp_die) {
+                       /* Address filtering 1: does sp_die include addr? */
+                       if (!dwarf_haspc(sp_die, addr))
+                               continue;
+                       /* Address filtering 2: No child include addr? */
+                       if (die_get_inlinefunc(sp_die, addr, &die_mem))
+                               continue;
+               }
 
-               ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
-               pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n",
-                        (int)i, (unsigned)lineno, (int)clm, addr);
+               pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n",
+                        (int)i, lineno, (unsigned long long)addr);
                pf->addr = addr;
-               /* Search a real subprogram including this line, */
-               ret = search_die_from_children(pf->cu_die,
-                                              probeaddr_callback, pf);
-               if (ret == 0)
-                       die("Probe point is not found in subprograms.");
+
+               show_probe_point(sp_die, pf);
                /* Continuing, because target line might be inlined. */
        }
-       dwarf_srclines_dealloc(__dw_debug, lines, cnt);
+       /* TODO: deallocate lines, but how? */
+}
+
+static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
+{
+       struct probe_finder *pf = (struct probe_finder *)data;
+       struct probe_point *pp = pf->pp;
+
+       if (pp->lazy_line)
+               find_probe_point_lazy(in_die, pf);
+       else {
+               /* Get probe address */
+               pf->addr = die_get_entrypc(in_die);
+               pf->addr += pp->offset;
+               pr_debug("found inline addr: 0x%jx\n",
+                        (uintmax_t)pf->addr);
+
+               show_probe_point(in_die, pf);
+       }
+
+       return DWARF_CB_OK;
 }
 
 /* Search function from function name */
-static int probefunc_callback(struct die_link *dlink, void *data)
+static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 {
        struct probe_finder *pf = (struct probe_finder *)data;
        struct probe_point *pp = pf->pp;
-       struct die_link *lk;
-       Dwarf_Signed offs;
-       Dwarf_Half tag;
-       int ret;
 
-       ret = dwarf_tag(dlink->die, &tag, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (tag == DW_TAG_subprogram) {
-               if (die_compare_name(dlink->die, pp->function) == 0) {
-                       if (pp->line) { /* Function relative line */
-                               pf->fno = die_get_decl_file(dlink->die);
-                               pf->lno = die_get_decl_line(dlink->die)
-                                        + pp->line;
-                               find_probe_point_by_line(pf);
-                               return 1;
-                       }
-                       if (die_inlined_subprogram(dlink->die)) {
-                               /* Inlined function, save it. */
-                               ret = dwarf_die_CU_offset(dlink->die,
-                                                         &pf->inl_offs,
-                                                         &__dw_error);
-                               DIE_IF(ret != DW_DLV_OK);
-                               pr_debug("inline definition offset %lld\n",
-                                        pf->inl_offs);
-                               return 0;       /* Continue to search */
-                       }
-                       /* Get probe address */
-                       pf->addr = die_get_entrypc(dlink->die);
+       /* Check tag and diename */
+       if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
+           die_compare_name(sp_die, pp->function) != 0)
+               return 0;
+
+       pf->fname = dwarf_decl_file(sp_die);
+       if (pp->line) { /* Function relative line */
+               dwarf_decl_line(sp_die, &pf->lno);
+               pf->lno += pp->line;
+               find_probe_point_by_line(pf);
+       } else if (!dwarf_func_inline(sp_die)) {
+               /* Real function */
+               if (pp->lazy_line)
+                       find_probe_point_lazy(sp_die, pf);
+               else {
+                       pf->addr = die_get_entrypc(sp_die);
                        pf->addr += pp->offset;
                        /* TODO: Check the address in this function */
-                       show_probepoint(dlink->die, pp->offset, pf);
-                       return 1; /* Exit; no same symbol in this CU. */
-               }
-       } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
-               if (die_get_abstract_origin(dlink->die) == pf->inl_offs) {
-                       /* Get probe address */
-                       pf->addr = die_get_entrypc(dlink->die);
-                       pf->addr += pp->offset;
-                       pr_debug("found inline addr: 0x%llx\n", pf->addr);
-                       /* Inlined function. Get a real subprogram */
-                       for (lk = dlink->parent; lk != NULL; lk = lk->parent) {
-                               tag = 0;
-                               dwarf_tag(lk->die, &tag, &__dw_error);
-                               DIE_IF(ret == DW_DLV_ERROR);
-                               if (tag == DW_TAG_subprogram &&
-                                   !die_inlined_subprogram(lk->die))
-                                       goto found;
-                       }
-                       die("Failed to find real subprogram.");
-found:
-                       /* Get offset from subprogram */
-                       ret = die_within_subprogram(lk->die, pf->addr, &offs);
-                       DIE_IF(!ret);
-                       show_probepoint(lk->die, offs, pf);
-                       /* Continue to search */
+                       show_probe_point(sp_die, pf);
                }
-       }
-       return 0;
+       } else
+               /* Inlined function: search instances */
+               dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf);
+
+       return 1; /* Exit; no same symbol in this CU. */
 }
 
 static void find_probe_point_by_func(struct probe_finder *pf)
 {
-       search_die_from_children(pf->cu_die, probefunc_callback, pf);
+       dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0);
 }
 
 /* Find a probe point */
-int find_probepoint(int fd, struct probe_point *pp)
+int find_probe_point(int fd, struct probe_point *pp)
 {
-       Dwarf_Half addr_size = 0;
-       Dwarf_Unsigned next_cuh = 0;
-       int cu_number = 0, ret;
        struct probe_finder pf = {.pp = pp};
+       int ret;
+       Dwarf_Off off, noff;
+       size_t cuhl;
+       Dwarf_Die *diep;
+       Dwarf *dbg;
 
-       ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
-       if (ret != DW_DLV_OK)
+       dbg = dwarf_begin(fd, DWARF_C_READ);
+       if (!dbg)
                return -ENOENT;
 
        pp->found = 0;
-       while (++cu_number) {
-               /* Search CU (Compilation Unit) */
-               ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
-                       &addr_size, &next_cuh, &__dw_error);
-               DIE_IF(ret == DW_DLV_ERROR);
-               if (ret == DW_DLV_NO_ENTRY)
-                       break;
-
+       off = 0;
+       line_list__init(&pf.lcache);
+       /* Loop on CUs (Compilation Unit) */
+       while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
                /* Get the DIE(Debugging Information Entry) of this CU */
-               ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
+               diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
+               if (!diep)
+                       continue;
 
                /* Check if target file is included. */
                if (pp->file)
-                       pf.fno = cu_find_fileno(pf.cu_die, pp->file);
+                       pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
+               else
+                       pf.fname = NULL;
 
-               if (!pp->file || pf.fno) {
+               if (!pp->file || pf.fname) {
                        /* Save CU base address (for frame_base) */
-                       ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error);
-                       DIE_IF(ret == DW_DLV_ERROR);
-                       if (ret == DW_DLV_NO_ENTRY)
+                       ret = dwarf_lowpc(&pf.cu_die, &pf.cu_base);
+                       if (ret != 0)
                                pf.cu_base = 0;
                        if (pp->function)
                                find_probe_point_by_func(&pf);
+                       else if (pp->lazy_line)
+                               find_probe_point_lazy(NULL, &pf);
                        else {
                                pf.lno = pp->line;
                                find_probe_point_by_line(&pf);
                        }
                }
-               dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
+               off = noff;
        }
-       ret = dwarf_finish(__dw_debug, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
+       line_list__free(&pf.lcache);
+       dwarf_end(dbg);
 
        return pp->found;
 }
 
-
-static void line_range_add_line(struct line_range *lr, unsigned int line)
-{
-       struct line_node *ln;
-       struct list_head *p;
-
-       /* Reverse search, because new line will be the last one */
-       list_for_each_entry_reverse(ln, &lr->line_list, list) {
-               if (ln->line < line) {
-                       p = &ln->list;
-                       goto found;
-               } else if (ln->line == line)    /* Already exist */
-                       return ;
-       }
-       /* List is empty, or the smallest entry */
-       p = &lr->line_list;
-found:
-       pr_debug("Debug: add a line %u\n", line);
-       ln = zalloc(sizeof(struct line_node));
-       DIE_IF(ln == NULL);
-       ln->line = line;
-       INIT_LIST_HEAD(&ln->list);
-       list_add(&ln->list, p);
-}
-
 /* Find line range from its line number */
-static void find_line_range_by_line(struct line_finder *lf)
+static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
 {
-       Dwarf_Signed cnt, i;
-       Dwarf_Line *lines;
-       Dwarf_Unsigned lineno = 0;
-       Dwarf_Unsigned fno;
+       Dwarf_Lines *lines;
+       Dwarf_Line *line;
+       size_t nlines, i;
        Dwarf_Addr addr;
+       int lineno;
        int ret;
+       const char *src;
+       Dwarf_Die die_mem;
 
-       ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
+       line_list__init(&lf->lr->line_list);
+       ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines);
+       DIE_IF(ret != 0);
 
-       for (i = 0; i < cnt; i++) {
-               ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
-               if (fno != lf->fno)
-                       continue;
-
-               ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
+       for (i = 0; i < nlines; i++) {
+               line = dwarf_onesrcline(lines, i);
+               ret = dwarf_lineno(line, &lineno);
+               DIE_IF(ret != 0);
                if (lf->lno_s > lineno || lf->lno_e < lineno)
                        continue;
 
-               /* Filter line in the function address range */
-               if (lf->addr_s && lf->addr_e) {
-                       ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
-                       DIE_IF(ret != DW_DLV_OK);
-                       if (lf->addr_s > addr || lf->addr_e <= addr)
+               if (sp_die) {
+                       /* Address filtering 1: does sp_die include addr? */
+                       ret = dwarf_lineaddr(line, &addr);
+                       DIE_IF(ret != 0);
+                       if (!dwarf_haspc(sp_die, addr))
+                               continue;
+
+                       /* Address filtering 2: No child include addr? */
+                       if (die_get_inlinefunc(sp_die, addr, &die_mem))
                                continue;
                }
-               line_range_add_line(lf->lr, (unsigned int)lineno);
+
+               /* TODO: Get fileno from line, but how? */
+               src = dwarf_linesrc(line, NULL, NULL);
+               if (strtailcmp(src, lf->fname) != 0)
+                       continue;
+
+               /* Copy real path */
+               if (!lf->lr->path)
+                       lf->lr->path = strdup(src);
+               line_list__add_line(&lf->lr->line_list, (unsigned int)lineno);
        }
-       dwarf_srclines_dealloc(__dw_debug, lines, cnt);
+       /* Update status */
        if (!list_empty(&lf->lr->line_list))
                lf->found = 1;
+       else {
+               free(lf->lr->path);
+               lf->lr->path = NULL;
+       }
+}
+
+static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
+{
+       find_line_range_by_line(in_die, (struct line_finder *)data);
+       return DWARF_CB_ABORT;  /* No need to find other instances */
 }
 
 /* Search function from function name */
-static int linefunc_callback(struct die_link *dlink, void *data)
+static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
 {
        struct line_finder *lf = (struct line_finder *)data;
        struct line_range *lr = lf->lr;
-       Dwarf_Half tag;
-       int ret;
 
-       ret = dwarf_tag(dlink->die, &tag, &__dw_error);
-       DIE_IF(ret == DW_DLV_ERROR);
-       if (tag == DW_TAG_subprogram &&
-           die_compare_name(dlink->die, lr->function) == 0) {
-               /* Get the address range of this function */
-               ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
-               if (ret == DW_DLV_OK)
-                       ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
-               DIE_IF(ret == DW_DLV_ERROR);
-               if (ret == DW_DLV_NO_ENTRY) {
-                       lf->addr_s = 0;
-                       lf->addr_e = 0;
-               }
-
-               lf->fno = die_get_decl_file(dlink->die);
-               lr->offset = die_get_decl_line(dlink->die);;
+       if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
+           die_compare_name(sp_die, lr->function) == 0) {
+               lf->fname = dwarf_decl_file(sp_die);
+               dwarf_decl_line(sp_die, &lr->offset);
+               pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
                lf->lno_s = lr->offset + lr->start;
                if (!lr->end)
-                       lf->lno_e = (Dwarf_Unsigned)-1;
+                       lf->lno_e = INT_MAX;
                else
                        lf->lno_e = lr->offset + lr->end;
                lr->start = lf->lno_s;
                lr->end = lf->lno_e;
-               find_line_range_by_line(lf);
-               /* If we find a target function, this should be end. */
-               lf->found = 1;
+               if (dwarf_func_inline(sp_die))
+                       dwarf_func_inline_instances(sp_die,
+                                                   line_range_inline_cb, lf);
+               else
+                       find_line_range_by_line(sp_die, lf);
                return 1;
        }
        return 0;
@@ -857,55 +779,55 @@ static int linefunc_callback(struct die_link *dlink, void *data)
 
 static void find_line_range_by_func(struct line_finder *lf)
 {
-       search_die_from_children(lf->cu_die, linefunc_callback, lf);
+       dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0);
 }
 
 int find_line_range(int fd, struct line_range *lr)
 {
-       Dwarf_Half addr_size = 0;
-       Dwarf_Unsigned next_cuh = 0;
+       struct line_finder lf = {.lr = lr, .found = 0};
        int ret;
-       struct line_finder lf = {.lr = lr};
+       Dwarf_Off off = 0, noff;
+       size_t cuhl;
+       Dwarf_Die *diep;
+       Dwarf *dbg;
 
-       ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
-       if (ret != DW_DLV_OK)
+       dbg = dwarf_begin(fd, DWARF_C_READ);
+       if (!dbg)
                return -ENOENT;
 
+       /* Loop on CUs (Compilation Unit) */
        while (!lf.found) {
-               /* Search CU (Compilation Unit) */
-               ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
-                       &addr_size, &next_cuh, &__dw_error);
-               DIE_IF(ret == DW_DLV_ERROR);
-               if (ret == DW_DLV_NO_ENTRY)
+               ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL);
+               if (ret != 0)
                        break;
 
                /* Get the DIE(Debugging Information Entry) of this CU */
-               ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
-               DIE_IF(ret != DW_DLV_OK);
+               diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die);
+               if (!diep)
+                       continue;
 
                /* Check if target file is included. */
                if (lr->file)
-                       lf.fno = cu_find_fileno(lf.cu_die, lr->file);
+                       lf.fname = cu_find_realpath(&lf.cu_die, lr->file);
+               else
+                       lf.fname = 0;
 
-               if (!lr->file || lf.fno) {
+               if (!lr->file || lf.fname) {
                        if (lr->function)
                                find_line_range_by_func(&lf);
                        else {
                                lf.lno_s = lr->start;
                                if (!lr->end)
-                                       lf.lno_e = (Dwarf_Unsigned)-1;
+                                       lf.lno_e = INT_MAX;
                                else
                                        lf.lno_e = lr->end;
-                               find_line_range_by_line(&lf);
+                               find_line_range_by_line(NULL, &lf);
                        }
-                       /* Get the real file path */
-                       if (lf.found)
-                               cu_get_filename(lf.cu_die, lf.fno, &lr->path);
                }
-               dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
+               off = noff;
        }
-       ret = dwarf_finish(__dw_debug, &__dw_error);
-       DIE_IF(ret != DW_DLV_OK);
+       pr_debug("path: %lx\n", (unsigned long)lr->path);
+       dwarf_end(dbg);
        return lf.found;
 }
 
index 972b386116f1b080bb96c5f38b96739ba0307f49..d1a651793ba6661bf88684e62978f697d5cbd9f6 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _PROBE_FINDER_H
 #define _PROBE_FINDER_H
 
+#include <stdbool.h>
 #include "util.h"
 
 #define MAX_PATH_LEN            256
@@ -20,6 +21,7 @@ struct probe_point {
        /* Inputs */
        char                    *file;                  /* File name */
        int                     line;                   /* Line number */
+       char                    *lazy_line;             /* Lazy line pattern */
 
        char                    *function;              /* Function name */
        int                     offset;                 /* Offset bytes */
@@ -46,53 +48,46 @@ struct line_range {
        char                    *function;              /* Function name */
        unsigned int            start;                  /* Start line number */
        unsigned int            end;                    /* End line number */
-       unsigned int            offset;                 /* Start line offset */
+       int                     offset;                 /* Start line offset */
        char                    *path;                  /* Real path name */
        struct list_head        line_list;              /* Visible lines */
 };
 
-#ifndef NO_LIBDWARF
-extern int find_probepoint(int fd, struct probe_point *pp);
+#ifndef NO_DWARF_SUPPORT
+extern int find_probe_point(int fd, struct probe_point *pp);
 extern int find_line_range(int fd, struct line_range *lr);
 
-/* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */
-#ifndef _MIPS_SZLONG
-# define _MIPS_SZLONG          0
-#endif
-
 #include <dwarf.h>
-#include <libdwarf.h>
+#include <libdw.h>
 
 struct probe_finder {
-       struct probe_point      *pp;                    /* Target probe point */
+       struct probe_point      *pp;            /* Target probe point */
 
        /* For function searching */
-       Dwarf_Addr              addr;                   /* Address */
-       Dwarf_Unsigned          fno;                    /* File number */
-       Dwarf_Unsigned          lno;                    /* Line number */
-       Dwarf_Off               inl_offs;               /* Inline offset */
-       Dwarf_Die               cu_die;                 /* Current CU */
+       Dwarf_Addr              addr;           /* Address */
+       const char              *fname;         /* File name */
+       int                     lno;            /* Line number */
+       Dwarf_Die               cu_die;         /* Current CU */
 
        /* For variable searching */
-       Dwarf_Addr              cu_base;                /* Current CU base address */
-       Dwarf_Locdesc           fbloc;                  /* Location of Current Frame Base */
-       const char              *var;                   /* Current variable name */
-       char                    *buf;                   /* Current output buffer */
-       int                     len;                    /* Length of output buffer */
+       Dwarf_Op                *fb_ops;        /* Frame base attribute */
+       Dwarf_Addr              cu_base;        /* Current CU base address */
+       const char              *var;           /* Current variable name */
+       char                    *buf;           /* Current output buffer */
+       int                     len;            /* Length of output buffer */
+       struct list_head        lcache;         /* Line cache for lazy match */
 };
 
 struct line_finder {
-       struct line_range       *lr;                    /* Target line range */
-
-       Dwarf_Unsigned          fno;                    /* File number */
-       Dwarf_Unsigned          lno_s;                  /* Start line number */
-       Dwarf_Unsigned          lno_e;                  /* End line number */
-       Dwarf_Addr              addr_s;                 /* Start address */
-       Dwarf_Addr              addr_e;                 /* End address */
-       Dwarf_Die               cu_die;                 /* Current CU */
+       struct line_range       *lr;            /* Target line range */
+
+       const char              *fname;         /* File name */
+       int                     lno_s;          /* Start line number */
+       int                     lno_e;          /* End line number */
+       Dwarf_Die               cu_die;         /* Current CU */
        int                     found;
 };
 
-#endif /* NO_LIBDWARF */
+#endif /* NO_DWARF_SUPPORT */
 
 #endif /*_PROBE_FINDER_H */
index c397d4f6f748279ee35b9b931e8e83add8e75967..a175949ed21643e1ebbb595f0212227a3c8c4f7d 100644 (file)
@@ -265,21 +265,21 @@ error:
        return false;
 }
 
-/**
- * strglobmatch - glob expression pattern matching
- * @str: the target string to match
- * @pat: the pattern string to match
- *
- * This returns true if the @str matches @pat. @pat can includes wildcards
- * ('*','?') and character classes ([CHARS], complementation and ranges are
- * also supported). Also, this supports escape character ('\') to use special
- * characters as normal character.
- *
- * Note: if @pat syntax is broken, this always returns false.
- */
-bool strglobmatch(const char *str, const char *pat)
+/* Glob/lazy pattern matching */
+static bool __match_glob(const char *str, const char *pat, bool ignore_space)
 {
        while (*str && *pat && *pat != '*') {
+               if (ignore_space) {
+                       /* Ignore spaces for lazy matching */
+                       if (isspace(*str)) {
+                               str++;
+                               continue;
+                       }
+                       if (isspace(*pat)) {
+                               pat++;
+                               continue;
+                       }
+               }
                if (*pat == '?') {      /* Matches any single character */
                        str++;
                        pat++;
@@ -308,3 +308,32 @@ bool strglobmatch(const char *str, const char *pat)
        return !*str && !*pat;
 }
 
+/**
+ * strglobmatch - glob expression pattern matching
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This returns true if the @str matches @pat. @pat can includes wildcards
+ * ('*','?') and character classes ([CHARS], complementation and ranges are
+ * also supported). Also, this supports escape character ('\') to use special
+ * characters as normal character.
+ *
+ * Note: if @pat syntax is broken, this always returns false.
+ */
+bool strglobmatch(const char *str, const char *pat)
+{
+       return __match_glob(str, pat, false);
+}
+
+/**
+ * strlazymatch - matching pattern strings lazily with glob pattern
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This is similar to strglobmatch, except this ignores spaces in
+ * the target string.
+ */
+bool strlazymatch(const char *str, const char *pat)
+{
+       return __match_glob(str, pat, true);
+}
index 02ede58c54b4e2593a1aa4e5d4f240d4912a55cf..542e44de37194ae78839ec6201b6c88777ec867d 100644 (file)
@@ -10,6 +10,7 @@ s64 perf_atoll(const char *str);
 char **argv_split(const char *str, int *argcp);
 void argv_free(char **argv);
 bool strglobmatch(const char *str, const char *pat);
+bool strlazymatch(const char *str, const char *pat);
 
 #define _STR(x) #x
 #define STR(x) _STR(x)
index daece36c0a5754a26af7e216b00e1d5ce71855a4..7f1178f6b839d42d96df35fd82ac26e36a03a15d 100644 (file)
@@ -12,3 +12,6 @@ config HAVE_KVM_EVENTFD
 
 config KVM_APIC_ARCHITECTURE
        bool
+
+config KVM_MMIO
+       bool
index f73de631e3eef53d6a5c50b95b21f037969d2f95..057e2cca6af5ed7a8c8cbdf4aa7c236713b3bf8f 100644 (file)
@@ -504,12 +504,12 @@ out:
 static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
                                      struct kvm_assigned_pci_dev *assigned_dev)
 {
-       int r = 0;
+       int r = 0, idx;
        struct kvm_assigned_dev_kernel *match;
        struct pci_dev *dev;
 
        mutex_lock(&kvm->lock);
-       down_read(&kvm->slots_lock);
+       idx = srcu_read_lock(&kvm->srcu);
 
        match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
                                      assigned_dev->assigned_dev_id);
@@ -526,7 +526,8 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
                r = -ENOMEM;
                goto out;
        }
-       dev = pci_get_bus_and_slot(assigned_dev->busnr,
+       dev = pci_get_domain_bus_and_slot(assigned_dev->segnr,
+                                  assigned_dev->busnr,
                                   assigned_dev->devfn);
        if (!dev) {
                printk(KERN_INFO "%s: host device not found\n", __func__);
@@ -548,6 +549,7 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
        pci_reset_function(dev);
 
        match->assigned_dev_id = assigned_dev->assigned_dev_id;
+       match->host_segnr = assigned_dev->segnr;
        match->host_busnr = assigned_dev->busnr;
        match->host_devfn = assigned_dev->devfn;
        match->flags = assigned_dev->flags;
@@ -573,7 +575,7 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
        }
 
 out:
-       up_read(&kvm->slots_lock);
+       srcu_read_unlock(&kvm->srcu, idx);
        mutex_unlock(&kvm->lock);
        return r;
 out_list_del:
@@ -585,7 +587,7 @@ out_put:
        pci_dev_put(dev);
 out_free:
        kfree(match);
-       up_read(&kvm->slots_lock);
+       srcu_read_unlock(&kvm->srcu, idx);
        mutex_unlock(&kvm->lock);
        return r;
 }
index 04d69cd7049b989100058dcb8d74ef6fb7c295e2..5169736377a3b88cb6d2c7abc7639e2da439cf64 100644 (file)
@@ -92,41 +92,64 @@ static const struct kvm_io_device_ops coalesced_mmio_ops = {
 int kvm_coalesced_mmio_init(struct kvm *kvm)
 {
        struct kvm_coalesced_mmio_dev *dev;
+       struct page *page;
        int ret;
 
+       ret = -ENOMEM;
+       page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+       if (!page)
+               goto out_err;
+       kvm->coalesced_mmio_ring = page_address(page);
+
+       ret = -ENOMEM;
        dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev), GFP_KERNEL);
        if (!dev)
-               return -ENOMEM;
+               goto out_free_page;
        spin_lock_init(&dev->lock);
        kvm_iodevice_init(&dev->dev, &coalesced_mmio_ops);
        dev->kvm = kvm;
        kvm->coalesced_mmio_dev = dev;
 
-       ret = kvm_io_bus_register_dev(kvm, &kvm->mmio_bus, &dev->dev);
+       mutex_lock(&kvm->slots_lock);
+       ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, &dev->dev);
+       mutex_unlock(&kvm->slots_lock);
        if (ret < 0)
-               kfree(dev);
+               goto out_free_dev;
+
+       return ret;
 
+out_free_dev:
+       kfree(dev);
+out_free_page:
+       __free_page(page);
+out_err:
        return ret;
 }
 
+void kvm_coalesced_mmio_free(struct kvm *kvm)
+{
+       if (kvm->coalesced_mmio_ring)
+               free_page((unsigned long)kvm->coalesced_mmio_ring);
+}
+
 int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
-                                        struct kvm_coalesced_mmio_zone *zone)
+                                        struct kvm_coalesced_mmio_zone *zone)
 {
        struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev;
 
        if (dev == NULL)
                return -EINVAL;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
        if (dev->nb_zones >= KVM_COALESCED_MMIO_ZONE_MAX) {
-               up_write(&kvm->slots_lock);
+               mutex_unlock(&kvm->slots_lock);
                return -ENOBUFS;
        }
 
        dev->zone[dev->nb_zones] = *zone;
        dev->nb_zones++;
 
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return 0;
 }
 
@@ -140,10 +163,10 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
        if (dev == NULL)
                return -EINVAL;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
 
        i = dev->nb_zones;
-       while(i) {
+       while (i) {
                z = &dev->zone[i - 1];
 
                /* unregister all zones
@@ -158,7 +181,7 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
                i--;
        }
 
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
 
        return 0;
 }
index 4b49f27fa31eb5cb4d38dc24eba58feaffa5ee3f..8a5959e3535f401482d866de6c8487e40138ea15 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef __KVM_COALESCED_MMIO_H__
+#define __KVM_COALESCED_MMIO_H__
+
 /*
  * KVM coalesced MMIO
  *
@@ -7,6 +10,8 @@
  *
  */
 
+#ifdef CONFIG_KVM_MMIO
+
 #define KVM_COALESCED_MMIO_ZONE_MAX 100
 
 struct kvm_coalesced_mmio_dev {
@@ -18,7 +23,17 @@ struct kvm_coalesced_mmio_dev {
 };
 
 int kvm_coalesced_mmio_init(struct kvm *kvm);
+void kvm_coalesced_mmio_free(struct kvm *kvm);
 int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
                                        struct kvm_coalesced_mmio_zone *zone);
 int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
                                          struct kvm_coalesced_mmio_zone *zone);
+
+#else
+
+static inline int kvm_coalesced_mmio_init(struct kvm *kvm) { return 0; }
+static inline void kvm_coalesced_mmio_free(struct kvm *kvm) { }
+
+#endif
+
+#endif
index a9d3fc6c681c5ab01bec43975d50ef93408c88e1..7016319b1ec0590513ba1886ede37982c39e71b4 100644 (file)
@@ -47,7 +47,6 @@ struct _irqfd {
        int                       gsi;
        struct list_head          list;
        poll_table                pt;
-       wait_queue_head_t        *wqh;
        wait_queue_t              wait;
        struct work_struct        inject;
        struct work_struct        shutdown;
@@ -159,8 +158,6 @@ irqfd_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh,
                        poll_table *pt)
 {
        struct _irqfd *irqfd = container_of(pt, struct _irqfd, pt);
-
-       irqfd->wqh = wqh;
        add_wait_queue(wqh, &irqfd->wait);
 }
 
@@ -463,7 +460,7 @@ static int
 kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
 {
        int                       pio = args->flags & KVM_IOEVENTFD_FLAG_PIO;
-       struct kvm_io_bus        *bus = pio ? &kvm->pio_bus : &kvm->mmio_bus;
+       enum kvm_bus              bus_idx = pio ? KVM_PIO_BUS : KVM_MMIO_BUS;
        struct _ioeventfd        *p;
        struct eventfd_ctx       *eventfd;
        int                       ret;
@@ -508,7 +505,7 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        else
                p->wildcard = true;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
 
        /* Verify that there isnt a match already */
        if (ioeventfd_check_collision(kvm, p)) {
@@ -518,18 +515,18 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
 
        kvm_iodevice_init(&p->dev, &ioeventfd_ops);
 
-       ret = __kvm_io_bus_register_dev(bus, &p->dev);
+       ret = kvm_io_bus_register_dev(kvm, bus_idx, &p->dev);
        if (ret < 0)
                goto unlock_fail;
 
        list_add_tail(&p->list, &kvm->ioeventfds);
 
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
 
        return 0;
 
 unlock_fail:
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
 
 fail:
        kfree(p);
@@ -542,7 +539,7 @@ static int
 kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
 {
        int                       pio = args->flags & KVM_IOEVENTFD_FLAG_PIO;
-       struct kvm_io_bus        *bus = pio ? &kvm->pio_bus : &kvm->mmio_bus;
+       enum kvm_bus              bus_idx = pio ? KVM_PIO_BUS : KVM_MMIO_BUS;
        struct _ioeventfd        *p, *tmp;
        struct eventfd_ctx       *eventfd;
        int                       ret = -ENOENT;
@@ -551,7 +548,7 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
        if (IS_ERR(eventfd))
                return PTR_ERR(eventfd);
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
 
        list_for_each_entry_safe(p, tmp, &kvm->ioeventfds, list) {
                bool wildcard = !(args->flags & KVM_IOEVENTFD_FLAG_DATAMATCH);
@@ -565,13 +562,13 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
                if (!p->wildcard && p->datamatch != args->datamatch)
                        continue;
 
-               __kvm_io_bus_unregister_dev(bus, &p->dev);
+               kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
                ioeventfd_release(p);
                ret = 0;
                break;
        }
 
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
 
        eventfd_ctx_put(eventfd);
 
index 38a2d20b89de93b4b6deb7f4de2850906de17243..3db15a807f8064149a8b3bf5f4fcdd349697cdc5 100644 (file)
@@ -100,6 +100,19 @@ static int ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx)
        return injected;
 }
 
+static void update_handled_vectors(struct kvm_ioapic *ioapic)
+{
+       DECLARE_BITMAP(handled_vectors, 256);
+       int i;
+
+       memset(handled_vectors, 0, sizeof(handled_vectors));
+       for (i = 0; i < IOAPIC_NUM_PINS; ++i)
+               __set_bit(ioapic->redirtbl[i].fields.vector, handled_vectors);
+       memcpy(ioapic->handled_vectors, handled_vectors,
+              sizeof(handled_vectors));
+       smp_wmb();
+}
+
 static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
 {
        unsigned index;
@@ -134,6 +147,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
                        e->bits |= (u32) val;
                        e->fields.remote_irr = 0;
                }
+               update_handled_vectors(ioapic);
                mask_after = e->fields.mask;
                if (mask_before != mask_after)
                        kvm_fire_mask_notifiers(ioapic->kvm, index, mask_after);
@@ -241,6 +255,9 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int trigger_mode)
 {
        struct kvm_ioapic *ioapic = kvm->arch.vioapic;
 
+       smp_rmb();
+       if (!test_bit(vector, ioapic->handled_vectors))
+               return;
        mutex_lock(&ioapic->lock);
        __kvm_ioapic_update_eoi(ioapic, vector, trigger_mode);
        mutex_unlock(&ioapic->lock);
@@ -352,6 +369,7 @@ void kvm_ioapic_reset(struct kvm_ioapic *ioapic)
        ioapic->ioregsel = 0;
        ioapic->irr = 0;
        ioapic->id = 0;
+       update_handled_vectors(ioapic);
 }
 
 static const struct kvm_io_device_ops ioapic_mmio_ops = {
@@ -372,13 +390,28 @@ int kvm_ioapic_init(struct kvm *kvm)
        kvm_ioapic_reset(ioapic);
        kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops);
        ioapic->kvm = kvm;
-       ret = kvm_io_bus_register_dev(kvm, &kvm->mmio_bus, &ioapic->dev);
-       if (ret < 0)
+       mutex_lock(&kvm->slots_lock);
+       ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, &ioapic->dev);
+       mutex_unlock(&kvm->slots_lock);
+       if (ret < 0) {
+               kvm->arch.vioapic = NULL;
                kfree(ioapic);
+       }
 
        return ret;
 }
 
+void kvm_ioapic_destroy(struct kvm *kvm)
+{
+       struct kvm_ioapic *ioapic = kvm->arch.vioapic;
+
+       if (ioapic) {
+               kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &ioapic->dev);
+               kvm->arch.vioapic = NULL;
+               kfree(ioapic);
+       }
+}
+
 int kvm_get_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state)
 {
        struct kvm_ioapic *ioapic = ioapic_irqchip(kvm);
@@ -399,6 +432,7 @@ int kvm_set_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state)
 
        mutex_lock(&ioapic->lock);
        memcpy(ioapic, state, sizeof(struct kvm_ioapic_state));
+       update_handled_vectors(ioapic);
        mutex_unlock(&ioapic->lock);
        return 0;
 }
index 419c43b667ab8503e8b0a1fc33c51bab99f42be3..8a751b78a4301bffda6139ce92cfda2c5d7317f5 100644 (file)
@@ -46,6 +46,7 @@ struct kvm_ioapic {
        struct kvm *kvm;
        void (*ack_notifier)(void *opaque, int irq);
        struct mutex lock;
+       DECLARE_BITMAP(handled_vectors, 256);
 };
 
 #ifdef DEBUG
@@ -71,6 +72,7 @@ int kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
 int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2);
 void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int trigger_mode);
 int kvm_ioapic_init(struct kvm *kvm);
+void kvm_ioapic_destroy(struct kvm *kvm);
 int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level);
 void kvm_ioapic_reset(struct kvm_ioapic *ioapic);
 int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
index 15147583abd164d64de465eb7fce9d0c27a41ef8..80fd3ad3b2de77d246fe8cb9a5df5714609735c3 100644 (file)
@@ -32,10 +32,10 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm);
 static void kvm_iommu_put_pages(struct kvm *kvm,
                                gfn_t base_gfn, unsigned long npages);
 
-int kvm_iommu_map_pages(struct kvm *kvm,
-                       gfn_t base_gfn, unsigned long npages)
+int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot)
 {
-       gfn_t gfn = base_gfn;
+       gfn_t gfn = slot->base_gfn;
+       unsigned long npages = slot->npages;
        pfn_t pfn;
        int i, r = 0;
        struct iommu_domain *domain = kvm->arch.iommu_domain;
@@ -54,7 +54,7 @@ int kvm_iommu_map_pages(struct kvm *kvm,
                if (iommu_iova_to_phys(domain, gfn_to_gpa(gfn)))
                        continue;
 
-               pfn = gfn_to_pfn(kvm, gfn);
+               pfn = gfn_to_pfn_memslot(kvm, slot, gfn);
                r = iommu_map_range(domain,
                                    gfn_to_gpa(gfn),
                                    pfn_to_hpa(pfn),
@@ -69,17 +69,19 @@ int kvm_iommu_map_pages(struct kvm *kvm,
        return 0;
 
 unmap_pages:
-       kvm_iommu_put_pages(kvm, base_gfn, i);
+       kvm_iommu_put_pages(kvm, slot->base_gfn, i);
        return r;
 }
 
 static int kvm_iommu_map_memslots(struct kvm *kvm)
 {
        int i, r = 0;
+       struct kvm_memslots *slots;
+
+       slots = rcu_dereference(kvm->memslots);
 
-       for (i = 0; i < kvm->nmemslots; i++) {
-               r = kvm_iommu_map_pages(kvm, kvm->memslots[i].base_gfn,
-                                       kvm->memslots[i].npages);
+       for (i = 0; i < slots->nmemslots; i++) {
+               r = kvm_iommu_map_pages(kvm, &slots->memslots[i]);
                if (r)
                        break;
        }
@@ -104,7 +106,8 @@ int kvm_assign_device(struct kvm *kvm,
 
        r = iommu_attach_device(domain, &pdev->dev);
        if (r) {
-               printk(KERN_ERR "assign device %x:%x.%x failed",
+               printk(KERN_ERR "assign device %x:%x:%x.%x failed",
+                       pci_domain_nr(pdev->bus),
                        pdev->bus->number,
                        PCI_SLOT(pdev->devfn),
                        PCI_FUNC(pdev->devfn));
@@ -125,7 +128,8 @@ int kvm_assign_device(struct kvm *kvm,
                        goto out_unmap;
        }
 
-       printk(KERN_DEBUG "assign device: host bdf = %x:%x:%x\n",
+       printk(KERN_DEBUG "assign device %x:%x:%x.%x\n",
+               assigned_dev->host_segnr,
                assigned_dev->host_busnr,
                PCI_SLOT(assigned_dev->host_devfn),
                PCI_FUNC(assigned_dev->host_devfn));
@@ -152,7 +156,8 @@ int kvm_deassign_device(struct kvm *kvm,
 
        iommu_detach_device(domain, &pdev->dev);
 
-       printk(KERN_DEBUG "deassign device: host bdf = %x:%x:%x\n",
+       printk(KERN_DEBUG "deassign device %x:%x:%x.%x\n",
+               assigned_dev->host_segnr,
                assigned_dev->host_busnr,
                PCI_SLOT(assigned_dev->host_devfn),
                PCI_FUNC(assigned_dev->host_devfn));
@@ -210,10 +215,13 @@ static void kvm_iommu_put_pages(struct kvm *kvm,
 static int kvm_iommu_unmap_memslots(struct kvm *kvm)
 {
        int i;
+       struct kvm_memslots *slots;
+
+       slots = rcu_dereference(kvm->memslots);
 
-       for (i = 0; i < kvm->nmemslots; i++) {
-               kvm_iommu_put_pages(kvm, kvm->memslots[i].base_gfn,
-                                   kvm->memslots[i].npages);
+       for (i = 0; i < slots->nmemslots; i++) {
+               kvm_iommu_put_pages(kvm, slots->memslots[i].base_gfn,
+                                   slots->memslots[i].npages);
        }
 
        return 0;
index a944be392d6e9420aa38d833cbc736bf9f07d22e..548f9253c1957f2d0088b74c01c015fee1e79b45 100644 (file)
@@ -44,6 +44,8 @@
 #include <linux/bitops.h>
 #include <linux/spinlock.h>
 #include <linux/compat.h>
+#include <linux/srcu.h>
+#include <linux/hugetlb.h>
 
 #include <asm/processor.h>
 #include <asm/io.h>
@@ -51,9 +53,7 @@
 #include <asm/pgtable.h>
 #include <asm-generic/bitops/le.h>
 
-#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
 #include "coalesced_mmio.h"
-#endif
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/kvm.h>
@@ -86,6 +86,8 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
 static int hardware_enable_all(void);
 static void hardware_disable_all(void);
 
+static void kvm_io_bus_destroy(struct kvm_io_bus *bus);
+
 static bool kvm_rebooting;
 
 static bool largepages_enabled = true;
@@ -136,7 +138,7 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req)
 
        zalloc_cpumask_var(&cpus, GFP_ATOMIC);
 
-       spin_lock(&kvm->requests_lock);
+       raw_spin_lock(&kvm->requests_lock);
        me = smp_processor_id();
        kvm_for_each_vcpu(i, vcpu, kvm) {
                if (test_and_set_bit(req, &vcpu->requests))
@@ -151,7 +153,7 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req)
                smp_call_function_many(cpus, ack_flush, NULL, 1);
        else
                called = false;
-       spin_unlock(&kvm->requests_lock);
+       raw_spin_unlock(&kvm->requests_lock);
        free_cpumask_var(cpus);
        return called;
 }
@@ -215,7 +217,7 @@ static void kvm_mmu_notifier_invalidate_page(struct mmu_notifier *mn,
                                             unsigned long address)
 {
        struct kvm *kvm = mmu_notifier_to_kvm(mn);
-       int need_tlb_flush;
+       int need_tlb_flush, idx;
 
        /*
         * When ->invalidate_page runs, the linux pte has been zapped
@@ -235,10 +237,12 @@ static void kvm_mmu_notifier_invalidate_page(struct mmu_notifier *mn,
         * pte after kvm_unmap_hva returned, without noticing the page
         * is going to be freed.
         */
+       idx = srcu_read_lock(&kvm->srcu);
        spin_lock(&kvm->mmu_lock);
        kvm->mmu_notifier_seq++;
        need_tlb_flush = kvm_unmap_hva(kvm, address);
        spin_unlock(&kvm->mmu_lock);
+       srcu_read_unlock(&kvm->srcu, idx);
 
        /* we've to flush the tlb before the pages can be freed */
        if (need_tlb_flush)
@@ -252,11 +256,14 @@ static void kvm_mmu_notifier_change_pte(struct mmu_notifier *mn,
                                        pte_t pte)
 {
        struct kvm *kvm = mmu_notifier_to_kvm(mn);
+       int idx;
 
+       idx = srcu_read_lock(&kvm->srcu);
        spin_lock(&kvm->mmu_lock);
        kvm->mmu_notifier_seq++;
        kvm_set_spte_hva(kvm, address, pte);
        spin_unlock(&kvm->mmu_lock);
+       srcu_read_unlock(&kvm->srcu, idx);
 }
 
 static void kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
@@ -265,8 +272,9 @@ static void kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
                                                    unsigned long end)
 {
        struct kvm *kvm = mmu_notifier_to_kvm(mn);
-       int need_tlb_flush = 0;
+       int need_tlb_flush = 0, idx;
 
+       idx = srcu_read_lock(&kvm->srcu);
        spin_lock(&kvm->mmu_lock);
        /*
         * The count increase must become visible at unlock time as no
@@ -277,6 +285,7 @@ static void kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
        for (; start < end; start += PAGE_SIZE)
                need_tlb_flush |= kvm_unmap_hva(kvm, start);
        spin_unlock(&kvm->mmu_lock);
+       srcu_read_unlock(&kvm->srcu, idx);
 
        /* we've to flush the tlb before the pages can be freed */
        if (need_tlb_flush)
@@ -314,11 +323,13 @@ static int kvm_mmu_notifier_clear_flush_young(struct mmu_notifier *mn,
                                              unsigned long address)
 {
        struct kvm *kvm = mmu_notifier_to_kvm(mn);
-       int young;
+       int young, idx;
 
+       idx = srcu_read_lock(&kvm->srcu);
        spin_lock(&kvm->mmu_lock);
        young = kvm_age_hva(kvm, address);
        spin_unlock(&kvm->mmu_lock);
+       srcu_read_unlock(&kvm->srcu, idx);
 
        if (young)
                kvm_flush_remote_tlbs(kvm);
@@ -341,15 +352,26 @@ static const struct mmu_notifier_ops kvm_mmu_notifier_ops = {
        .change_pte             = kvm_mmu_notifier_change_pte,
        .release                = kvm_mmu_notifier_release,
 };
+
+static int kvm_init_mmu_notifier(struct kvm *kvm)
+{
+       kvm->mmu_notifier.ops = &kvm_mmu_notifier_ops;
+       return mmu_notifier_register(&kvm->mmu_notifier, current->mm);
+}
+
+#else  /* !(CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER) */
+
+static int kvm_init_mmu_notifier(struct kvm *kvm)
+{
+       return 0;
+}
+
 #endif /* CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER */
 
 static struct kvm *kvm_create_vm(void)
 {
-       int r = 0;
+       int r = 0, i;
        struct kvm *kvm = kvm_arch_create_vm();
-#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
-       struct page *page;
-#endif
 
        if (IS_ERR(kvm))
                goto out;
@@ -363,39 +385,35 @@ static struct kvm *kvm_create_vm(void)
        INIT_HLIST_HEAD(&kvm->irq_ack_notifier_list);
 #endif
 
-#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
-       page = alloc_page(GFP_KERNEL | __GFP_ZERO);
-       if (!page) {
-               r = -ENOMEM;
+       r = -ENOMEM;
+       kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+       if (!kvm->memslots)
                goto out_err;
-       }
-       kvm->coalesced_mmio_ring =
-                       (struct kvm_coalesced_mmio_ring *)page_address(page);
-#endif
-
-#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
-       {
-               kvm->mmu_notifier.ops = &kvm_mmu_notifier_ops;
-               r = mmu_notifier_register(&kvm->mmu_notifier, current->mm);
-               if (r) {
-#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
-                       put_page(page);
-#endif
+       if (init_srcu_struct(&kvm->srcu))
+               goto out_err;
+       for (i = 0; i < KVM_NR_BUSES; i++) {
+               kvm->buses[i] = kzalloc(sizeof(struct kvm_io_bus),
+                                       GFP_KERNEL);
+               if (!kvm->buses[i]) {
+                       cleanup_srcu_struct(&kvm->srcu);
                        goto out_err;
                }
        }
-#endif
+
+       r = kvm_init_mmu_notifier(kvm);
+       if (r) {
+               cleanup_srcu_struct(&kvm->srcu);
+               goto out_err;
+       }
 
        kvm->mm = current->mm;
        atomic_inc(&kvm->mm->mm_count);
        spin_lock_init(&kvm->mmu_lock);
-       spin_lock_init(&kvm->requests_lock);
-       kvm_io_bus_init(&kvm->pio_bus);
+       raw_spin_lock_init(&kvm->requests_lock);
        kvm_eventfd_init(kvm);
        mutex_init(&kvm->lock);
        mutex_init(&kvm->irq_lock);
-       kvm_io_bus_init(&kvm->mmio_bus);
-       init_rwsem(&kvm->slots_lock);
+       mutex_init(&kvm->slots_lock);
        atomic_set(&kvm->users_count, 1);
        spin_lock(&kvm_lock);
        list_add(&kvm->vm_list, &vm_list);
@@ -406,12 +424,12 @@ static struct kvm *kvm_create_vm(void)
 out:
        return kvm;
 
-#if defined(KVM_COALESCED_MMIO_PAGE_OFFSET) || \
-    (defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER))
 out_err:
        hardware_disable_all();
-#endif
 out_err_nodisable:
+       for (i = 0; i < KVM_NR_BUSES; i++)
+               kfree(kvm->buses[i]);
+       kfree(kvm->memslots);
        kfree(kvm);
        return ERR_PTR(r);
 }
@@ -446,13 +464,17 @@ static void kvm_free_physmem_slot(struct kvm_memory_slot *free,
 void kvm_free_physmem(struct kvm *kvm)
 {
        int i;
+       struct kvm_memslots *slots = kvm->memslots;
+
+       for (i = 0; i < slots->nmemslots; ++i)
+               kvm_free_physmem_slot(&slots->memslots[i], NULL);
 
-       for (i = 0; i < kvm->nmemslots; ++i)
-               kvm_free_physmem_slot(&kvm->memslots[i], NULL);
+       kfree(kvm->memslots);
 }
 
 static void kvm_destroy_vm(struct kvm *kvm)
 {
+       int i;
        struct mm_struct *mm = kvm->mm;
 
        kvm_arch_sync_events(kvm);
@@ -460,12 +482,9 @@ static void kvm_destroy_vm(struct kvm *kvm)
        list_del(&kvm->vm_list);
        spin_unlock(&kvm_lock);
        kvm_free_irq_routing(kvm);
-       kvm_io_bus_destroy(&kvm->pio_bus);
-       kvm_io_bus_destroy(&kvm->mmio_bus);
-#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
-       if (kvm->coalesced_mmio_ring != NULL)
-               free_page((unsigned long)kvm->coalesced_mmio_ring);
-#endif
+       for (i = 0; i < KVM_NR_BUSES; i++)
+               kvm_io_bus_destroy(kvm->buses[i]);
+       kvm_coalesced_mmio_free(kvm);
 #if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
        mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm);
 #else
@@ -512,12 +531,13 @@ int __kvm_set_memory_region(struct kvm *kvm,
                            struct kvm_userspace_memory_region *mem,
                            int user_alloc)
 {
-       int r;
+       int r, flush_shadow = 0;
        gfn_t base_gfn;
        unsigned long npages;
        unsigned long i;
        struct kvm_memory_slot *memslot;
        struct kvm_memory_slot old, new;
+       struct kvm_memslots *slots, *old_memslots;
 
        r = -EINVAL;
        /* General sanity checks */
@@ -532,7 +552,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
        if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr)
                goto out;
 
-       memslot = &kvm->memslots[mem->slot];
+       memslot = &kvm->memslots->memslots[mem->slot];
        base_gfn = mem->guest_phys_addr >> PAGE_SHIFT;
        npages = mem->memory_size >> PAGE_SHIFT;
 
@@ -553,7 +573,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
        /* Check for overlaps */
        r = -EEXIST;
        for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
-               struct kvm_memory_slot *s = &kvm->memslots[i];
+               struct kvm_memory_slot *s = &kvm->memslots->memslots[i];
 
                if (s == memslot || !s->npages)
                        continue;
@@ -579,15 +599,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
                memset(new.rmap, 0, npages * sizeof(*new.rmap));
 
                new.user_alloc = user_alloc;
-               /*
-                * hva_to_rmmap() serialzies with the mmu_lock and to be
-                * safe it has to ignore memslots with !user_alloc &&
-                * !userspace_addr.
-                */
-               if (user_alloc)
-                       new.userspace_addr = mem->userspace_addr;
-               else
-                       new.userspace_addr = 0;
+               new.userspace_addr = mem->userspace_addr;
        }
        if (!npages)
                goto skip_lpage;
@@ -642,8 +654,9 @@ skip_lpage:
                if (!new.dirty_bitmap)
                        goto out_free;
                memset(new.dirty_bitmap, 0, dirty_bytes);
+               /* destroy any largepage mappings for dirty tracking */
                if (old.npages)
-                       kvm_arch_flush_shadow(kvm);
+                       flush_shadow = 1;
        }
 #else  /* not defined CONFIG_S390 */
        new.user_alloc = user_alloc;
@@ -651,36 +664,72 @@ skip_lpage:
                new.userspace_addr = mem->userspace_addr;
 #endif /* not defined CONFIG_S390 */
 
-       if (!npages)
+       if (!npages) {
+               r = -ENOMEM;
+               slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+               if (!slots)
+                       goto out_free;
+               memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
+               if (mem->slot >= slots->nmemslots)
+                       slots->nmemslots = mem->slot + 1;
+               slots->memslots[mem->slot].flags |= KVM_MEMSLOT_INVALID;
+
+               old_memslots = kvm->memslots;
+               rcu_assign_pointer(kvm->memslots, slots);
+               synchronize_srcu_expedited(&kvm->srcu);
+               /* From this point no new shadow pages pointing to a deleted
+                * memslot will be created.
+                *
+                * validation of sp->gfn happens in:
+                *      - gfn_to_hva (kvm_read_guest, gfn_to_pfn)
+                *      - kvm_is_visible_gfn (mmu_check_roots)
+                */
                kvm_arch_flush_shadow(kvm);
+               kfree(old_memslots);
+       }
 
-       spin_lock(&kvm->mmu_lock);
-       if (mem->slot >= kvm->nmemslots)
-               kvm->nmemslots = mem->slot + 1;
-
-       *memslot = new;
-       spin_unlock(&kvm->mmu_lock);
-
-       r = kvm_arch_set_memory_region(kvm, mem, old, user_alloc);
-       if (r) {
-               spin_lock(&kvm->mmu_lock);
-               *memslot = old;
-               spin_unlock(&kvm->mmu_lock);
+       r = kvm_arch_prepare_memory_region(kvm, &new, old, mem, user_alloc);
+       if (r)
                goto out_free;
-       }
 
-       kvm_free_physmem_slot(&old, npages ? &new : NULL);
-       /* Slot deletion case: we have to update the current slot */
-       spin_lock(&kvm->mmu_lock);
-       if (!npages)
-               *memslot = old;
-       spin_unlock(&kvm->mmu_lock);
 #ifdef CONFIG_DMAR
        /* map the pages in iommu page table */
-       r = kvm_iommu_map_pages(kvm, base_gfn, npages);
-       if (r)
-               goto out;
+       if (npages) {
+               r = kvm_iommu_map_pages(kvm, &new);
+               if (r)
+                       goto out_free;
+       }
 #endif
+
+       r = -ENOMEM;
+       slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+       if (!slots)
+               goto out_free;
+       memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
+       if (mem->slot >= slots->nmemslots)
+               slots->nmemslots = mem->slot + 1;
+
+       /* actual memory is freed via old in kvm_free_physmem_slot below */
+       if (!npages) {
+               new.rmap = NULL;
+               new.dirty_bitmap = NULL;
+               for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i)
+                       new.lpage_info[i] = NULL;
+       }
+
+       slots->memslots[mem->slot] = new;
+       old_memslots = kvm->memslots;
+       rcu_assign_pointer(kvm->memslots, slots);
+       synchronize_srcu_expedited(&kvm->srcu);
+
+       kvm_arch_commit_memory_region(kvm, mem, old, user_alloc);
+
+       kvm_free_physmem_slot(&old, &new);
+       kfree(old_memslots);
+
+       if (flush_shadow)
+               kvm_arch_flush_shadow(kvm);
+
        return 0;
 
 out_free:
@@ -697,9 +746,9 @@ int kvm_set_memory_region(struct kvm *kvm,
 {
        int r;
 
-       down_write(&kvm->slots_lock);
+       mutex_lock(&kvm->slots_lock);
        r = __kvm_set_memory_region(kvm, mem, user_alloc);
-       up_write(&kvm->slots_lock);
+       mutex_unlock(&kvm->slots_lock);
        return r;
 }
 EXPORT_SYMBOL_GPL(kvm_set_memory_region);
@@ -726,7 +775,7 @@ int kvm_get_dirty_log(struct kvm *kvm,
        if (log->slot >= KVM_MEMORY_SLOTS)
                goto out;
 
-       memslot = &kvm->memslots[log->slot];
+       memslot = &kvm->memslots->memslots[log->slot];
        r = -ENOENT;
        if (!memslot->dirty_bitmap)
                goto out;
@@ -780,9 +829,10 @@ EXPORT_SYMBOL_GPL(kvm_is_error_hva);
 struct kvm_memory_slot *gfn_to_memslot_unaliased(struct kvm *kvm, gfn_t gfn)
 {
        int i;
+       struct kvm_memslots *slots = rcu_dereference(kvm->memslots);
 
-       for (i = 0; i < kvm->nmemslots; ++i) {
-               struct kvm_memory_slot *memslot = &kvm->memslots[i];
+       for (i = 0; i < slots->nmemslots; ++i) {
+               struct kvm_memory_slot *memslot = &slots->memslots[i];
 
                if (gfn >= memslot->base_gfn
                    && gfn < memslot->base_gfn + memslot->npages)
@@ -801,10 +851,14 @@ struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
 int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn)
 {
        int i;
+       struct kvm_memslots *slots = rcu_dereference(kvm->memslots);
 
-       gfn = unalias_gfn(kvm, gfn);
+       gfn = unalias_gfn_instantiation(kvm, gfn);
        for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
-               struct kvm_memory_slot *memslot = &kvm->memslots[i];
+               struct kvm_memory_slot *memslot = &slots->memslots[i];
+
+               if (memslot->flags & KVM_MEMSLOT_INVALID)
+                       continue;
 
                if (gfn >= memslot->base_gfn
                    && gfn < memslot->base_gfn + memslot->npages)
@@ -814,33 +868,68 @@ int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn)
 }
 EXPORT_SYMBOL_GPL(kvm_is_visible_gfn);
 
+unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn)
+{
+       struct vm_area_struct *vma;
+       unsigned long addr, size;
+
+       size = PAGE_SIZE;
+
+       addr = gfn_to_hva(kvm, gfn);
+       if (kvm_is_error_hva(addr))
+               return PAGE_SIZE;
+
+       down_read(&current->mm->mmap_sem);
+       vma = find_vma(current->mm, addr);
+       if (!vma)
+               goto out;
+
+       size = vma_kernel_pagesize(vma);
+
+out:
+       up_read(&current->mm->mmap_sem);
+
+       return size;
+}
+
+int memslot_id(struct kvm *kvm, gfn_t gfn)
+{
+       int i;
+       struct kvm_memslots *slots = rcu_dereference(kvm->memslots);
+       struct kvm_memory_slot *memslot = NULL;
+
+       gfn = unalias_gfn(kvm, gfn);
+       for (i = 0; i < slots->nmemslots; ++i) {
+               memslot = &slots->memslots[i];
+
+               if (gfn >= memslot->base_gfn
+                   && gfn < memslot->base_gfn + memslot->npages)
+                       break;
+       }
+
+       return memslot - slots->memslots;
+}
+
 unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn)
 {
        struct kvm_memory_slot *slot;
 
-       gfn = unalias_gfn(kvm, gfn);
+       gfn = unalias_gfn_instantiation(kvm, gfn);
        slot = gfn_to_memslot_unaliased(kvm, gfn);
-       if (!slot)
+       if (!slot || slot->flags & KVM_MEMSLOT_INVALID)
                return bad_hva();
        return (slot->userspace_addr + (gfn - slot->base_gfn) * PAGE_SIZE);
 }
 EXPORT_SYMBOL_GPL(gfn_to_hva);
 
-pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
+static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)
 {
        struct page *page[1];
-       unsigned long addr;
        int npages;
        pfn_t pfn;
 
        might_sleep();
 
-       addr = gfn_to_hva(kvm, gfn);
-       if (kvm_is_error_hva(addr)) {
-               get_page(bad_page);
-               return page_to_pfn(bad_page);
-       }
-
        npages = get_user_pages_fast(addr, 1, 1, page);
 
        if (unlikely(npages != 1)) {
@@ -865,8 +954,32 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
        return pfn;
 }
 
+pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
+{
+       unsigned long addr;
+
+       addr = gfn_to_hva(kvm, gfn);
+       if (kvm_is_error_hva(addr)) {
+               get_page(bad_page);
+               return page_to_pfn(bad_page);
+       }
+
+       return hva_to_pfn(kvm, addr);
+}
 EXPORT_SYMBOL_GPL(gfn_to_pfn);
 
+static unsigned long gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
+{
+       return (slot->userspace_addr + (gfn - slot->base_gfn) * PAGE_SIZE);
+}
+
+pfn_t gfn_to_pfn_memslot(struct kvm *kvm,
+                        struct kvm_memory_slot *slot, gfn_t gfn)
+{
+       unsigned long addr = gfn_to_hva_memslot(slot, gfn);
+       return hva_to_pfn(kvm, addr);
+}
+
 struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
 {
        pfn_t pfn;
@@ -1854,12 +1967,7 @@ static struct notifier_block kvm_reboot_notifier = {
        .priority = 0,
 };
 
-void kvm_io_bus_init(struct kvm_io_bus *bus)
-{
-       memset(bus, 0, sizeof(*bus));
-}
-
-void kvm_io_bus_destroy(struct kvm_io_bus *bus)
+static void kvm_io_bus_destroy(struct kvm_io_bus *bus)
 {
        int i;
 
@@ -1868,13 +1976,15 @@ void kvm_io_bus_destroy(struct kvm_io_bus *bus)
 
                kvm_iodevice_destructor(pos);
        }
+       kfree(bus);
 }
 
 /* kvm_io_bus_write - called under kvm->slots_lock */
-int kvm_io_bus_write(struct kvm_io_bus *bus, gpa_t addr,
+int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
                     int len, const void *val)
 {
        int i;
+       struct kvm_io_bus *bus = rcu_dereference(kvm->buses[bus_idx]);
        for (i = 0; i < bus->dev_count; i++)
                if (!kvm_iodevice_write(bus->devs[i], addr, len, val))
                        return 0;
@@ -1882,59 +1992,71 @@ int kvm_io_bus_write(struct kvm_io_bus *bus, gpa_t addr,
 }
 
 /* kvm_io_bus_read - called under kvm->slots_lock */
-int kvm_io_bus_read(struct kvm_io_bus *bus, gpa_t addr, int len, void *val)
+int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
+                   int len, void *val)
 {
        int i;
+       struct kvm_io_bus *bus = rcu_dereference(kvm->buses[bus_idx]);
+
        for (i = 0; i < bus->dev_count; i++)
                if (!kvm_iodevice_read(bus->devs[i], addr, len, val))
                        return 0;
        return -EOPNOTSUPP;
 }
 
-int kvm_io_bus_register_dev(struct kvm *kvm, struct kvm_io_bus *bus,
-                            struct kvm_io_device *dev)
+/* Caller must hold slots_lock. */
+int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+                           struct kvm_io_device *dev)
 {
-       int ret;
-
-       down_write(&kvm->slots_lock);
-       ret = __kvm_io_bus_register_dev(bus, dev);
-       up_write(&kvm->slots_lock);
+       struct kvm_io_bus *new_bus, *bus;
 
-       return ret;
-}
-
-/* An unlocked version. Caller must have write lock on slots_lock. */
-int __kvm_io_bus_register_dev(struct kvm_io_bus *bus,
-                             struct kvm_io_device *dev)
-{
+       bus = kvm->buses[bus_idx];
        if (bus->dev_count > NR_IOBUS_DEVS-1)
                return -ENOSPC;
 
-       bus->devs[bus->dev_count++] = dev;
+       new_bus = kzalloc(sizeof(struct kvm_io_bus), GFP_KERNEL);
+       if (!new_bus)
+               return -ENOMEM;
+       memcpy(new_bus, bus, sizeof(struct kvm_io_bus));
+       new_bus->devs[new_bus->dev_count++] = dev;
+       rcu_assign_pointer(kvm->buses[bus_idx], new_bus);
+       synchronize_srcu_expedited(&kvm->srcu);
+       kfree(bus);
 
        return 0;
 }
 
-void kvm_io_bus_unregister_dev(struct kvm *kvm,
-                              struct kvm_io_bus *bus,
-                              struct kvm_io_device *dev)
+/* Caller must hold slots_lock. */
+int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+                             struct kvm_io_device *dev)
 {
-       down_write(&kvm->slots_lock);
-       __kvm_io_bus_unregister_dev(bus, dev);
-       up_write(&kvm->slots_lock);
-}
+       int i, r;
+       struct kvm_io_bus *new_bus, *bus;
 
-/* An unlocked version. Caller must have write lock on slots_lock. */
-void __kvm_io_bus_unregister_dev(struct kvm_io_bus *bus,
-                                struct kvm_io_device *dev)
-{
-       int i;
+       new_bus = kzalloc(sizeof(struct kvm_io_bus), GFP_KERNEL);
+       if (!new_bus)
+               return -ENOMEM;
 
-       for (i = 0; i < bus->dev_count; i++)
-               if (bus->devs[i] == dev) {
-                       bus->devs[i] = bus->devs[--bus->dev_count];
+       bus = kvm->buses[bus_idx];
+       memcpy(new_bus, bus, sizeof(struct kvm_io_bus));
+
+       r = -ENOENT;
+       for (i = 0; i < new_bus->dev_count; i++)
+               if (new_bus->devs[i] == dev) {
+                       r = 0;
+                       new_bus->devs[i] = new_bus->devs[--new_bus->dev_count];
                        break;
                }
+
+       if (r) {
+               kfree(new_bus);
+               return r;
+       }
+
+       rcu_assign_pointer(kvm->buses[bus_idx], new_bus);
+       synchronize_srcu_expedited(&kvm->srcu);
+       kfree(bus);
+       return r;
 }
 
 static struct notifier_block kvm_cpu_notifier = {